An 'Ideal' Machine Language Save For The Pet
Arthur C. Hudson
To paraphrase Peter Ustinov: there are some things that can be done, and some things that can not be done. There are even ways of doing the things that can not be done.
There is a way to partition the PET, so that it thinks that it is two (or more) computers. Among the things that we can do with this technique is to write a Basic program which efficiently incorporates and saves its own machine language.
Let us start immediately with an example of what to do, leaving the explanation until later. If you want to follow this procedure on your PET, use exactly the order given.
Suppose that we need space for 239 or less bytes of ML (machine language). Start with a cold or reset PET, and enter, using no spaces:
10POKE041,5 : RUN (This is for upgrade ROM, if you have old ROM use 123 in place of 041.)
Now examine the contents of the SB (start-of-basic) pointer. If you have upgrade ROM this pointer is at bins $0028,0029 (40,41 in decimal). On the old ROM it is at bins $007A,007B (122, 123). You will find that this pointer contains 01,04; in other words it points to bin $0401, the second bin in page 4. (In decimal, 1025 = 1 + 4*256). Using a direct statement, or the resident monitor if you have it, change the pointer to point one full page higher, i.e. change it to read 01,05. Also change the three immediately following pointers to page 5 instead of page 4.
When the PET was first turned on (but not now) the three bins starting at $0400 each contained zero. We must now poke or load a similar group of three zeros in higher RAM at bins which are higher than $0400 by exactly the amount we have incremented the SB pointer. In the example this means insert three zeros starting at bin $0500 (1280 in decimal).
ML routines may now be loaded anywhere between bins $0410 and $04FF. For our example we borrow Raynor Taylor's 'Tiny' (PET User Notes V. 1. No. 4). Starting at bin $0410 (1040) we load the following:
A2 00 8A 9D 50 81 E8 D0 F9 60
Now enter any basic statements you wish, you may even re-use 10 as a line number. Include at least one SYS1040 to access the ML. Here is a short suggested program which demonstrates that For Loops, String Arrays etc. and also the machine language function normally:
10 A$ = "PARTITIONED BY A. HUDSON 8 SEPT. '80 20 DIML$(35) 30 FORI = 1TO35 : L$(I) = LEFT$ (A$,I): NEXT 40 FORI = 1TO35 : ?L$(I) : NEXT 50 SYS1040 : END 999 POKE41, 4 : SAVE "PARTITION DEMO
Now just key in RUN and stand well back!
All that remains now is the Save procedure. The only departure from the usual Basic Save is that we must first change the SB pointer back to its normal value which is 01, 04. In the above program we do this automatically with RUN999. In a fully developed program (if there is such a thing) statement 999 could be omitted.
One word of caution: RAM should contain three zeros starting at $040D. If you have inadvertently disturbed these, put them back before the Save.
What we have done is partition the PET into two parts. When the SP pointer points to $0401 we are in partition 1. The only Basic we permit here is the simple Poke and Run. Do not try to change this in any way. You can list immediately after loading and you will see this statement. You may admire it, but don't change it!
Partition 1 extends from the fourth bin beyond the last bin used by the Poke and Run to the last bin but one before the bin pointed to by the modified SB pointer. This last bin must contain a zero. We could dispense with the Poke and Run, but then we would have to poke the SB pointer manually after each load. ML may be loaded and altered at will, when the pointer points to partition 1, but Basic can not.
Our schizophrenic PET is in a much more amenable mood in partition 2, here it can be regarded as completely normal - we can fiddle with the Basic to our heart's content, just remembering to poke back to partition 1 before saving. Basic here can reach into partition 1 to access any ML programs which reside there.
Now what about the size of partition 1. To increase it by 256 bytes, just up the pointer high addresses to 6 instead of 5 which was used in the example. This can be continued until you run of memory. To allot a smaller ML space the low order addresses of the pointers must be augmented by a constant, but the same principle applies. If you are very short of memory and want to tailor partition 1 to exactly the amount of ML needed, both parts of the pointers may have to be changed and a double Poke and Run used. This is trickier, but there is no real barrier.
You will almost certainly get into trouble if you try to change horses in mid stream, that is if you try to change the size of the partition after you have started to program. The best way to stay out of trouble is to decide initially that 239 bytes, or 239 plus some multiple of 256 is adequate. Then you never need to touch the low order parts of the four pointers.
With care, a tape made for old ROM can be loaded on a PET using new ROM, but considerable pointer modification is necessary. In the Poke and Run, I used 041 rather than 41 so that the statement is the same length for old and new ROM.