Okay, 6809 folks… In my 64K TRS-80 CoCo memory test article, I used an assembly language program of unknown origin to copy the CoCo’s ROM in to RAM. The “test” part is POKEing a byte in to ROM space and seeing if it now changes (since, on a 64K system, it would be running out of RAM).
That code looks like it was built for speed, moving 6 bytes at a time by using three 16-bit registers (X, Y and U). As a refresher, here is that code:
start: PSHS CC ORCC #$50 LDY #$8000 loop1: STA $FFDE LDD ,Y LDX $02,Y LDU $04,Y STA $FFDF STD ,Y++ STX ,Y++ STU ,Y++ loop2: CMPY #$FEFC BCS loop1 CMPY #$FF00 BCC done STA $FFDE LDD ,Y STA $FFDF STD ,Y++ BRA loop2 done: PULS CC RTS
I presented this routine as a BASIC loader program so one could easily type it in rather than needing an assembler and typing in assembly source code to compile.
For folks patient enough to type in a whole CoCo screen full of hexadecimal DATA statements, it works fine. But I thought it might be finer to present an even smaller program with less DATA statements.
The program size can almost be cut in half by eliminating the first loop that copies the 6 bytes at a time. Instead, I came up with something like this:
start: PSHS CC Save CC ORCC #$50 Mask interrupts LDX #$8000 Start of ROM loop: STA $FFDE Enable ROM LDD ,X Load D with whatever is at X STA $FFDF Disable ROM STD ,X++ Store D at X and increment X CMPX #$FF00 Is X past end of ROM? BNE loop If not, repeat PULS CC Restore CC RTS Return
The original “need for speed” version compiled to 53 bytes. This new version compiles to 25 bytes. That would make it much easier to type in, like this:
0 REM 64K ROM TO RAM (25) 10 FOR L=16128 TO 16152 20 READ V:POKE L,V 30 NEXT 40 EXEC 16128:POKE 44015,89 80 DATA 52,1,26,80,142,128,0 90 DATA 183,255,222,236,132 100 DATA 183,255,223,237,129 110 DATA 140,255,0,38,241,53 120 DATA 1,57
Is there anything I can do to save a few bytes in that ROM to RAM routine? Please share comments and suggestions.
Is there an easier way to detect 64K? (Hey, that rhymes!)
If the goal is to just test for the existence of 64K, all we really need to do is put the machine in RAM mode and try to modify a byte in the upper 32K. If it can be modified, 64K is there. Maybe there is an even smaller way just to do that?
In 64K, you have memory locations 0-65535 available ($0000-$FFFF). But, the last 255 bytes ($FF00-$FFFF) are used for I/O, and as far as I know, the RAM there cannot be accessed. (Is this correct?) That would mean the last byte of usable RAM on a 64K CoCo would be at $FF00-1 ($FEFF). If that is correct, all we need to do is switch to RAM mode, store a byte at $FEFF and then read it back and see if it is what we put there. If it is, 64K exists.
There is a ROM routine that will output whatever character is loaded in the A register. We could use that to print out a message if 64K exists. Since the goal is to make this as small as possible, the message could simply be ‘Y’. My first attempt was something like this:
start: PSHS CC Save CC ORCC #$50 Mask interrupts. STA $FFDF Disable ROM LDA #'Y Load A with 'Y' STA $FEFF Store A in last RAM byte CLRA Clear A LDA $FEFF Load A with last RAM byte CMPA #'Y Compare to 'Y' BEQ done If Y, done. LDA #'N Else, load A with 'N' done: STA $FFDE Enable ROM PULS CC Restore CC JSR [$A002] Output byte in A to console. RTS Return
I disable the ROM (going in to “all RAM mode”), load A with a ‘Y’ character, then store it at $FEFF. I then clear A, then load A with whatever is at $FEFF. I compare A with ‘Y’ and if it is, I branch to the end where it will re-enable ROM, restore the CC register, then jump to the ROM routine that outputs whatever is in A. If it had no equaled ‘Y’, it would have not branched to ‘done’ and would instead load A with ‘N’, then complete, outputting ‘N’.
Running that should print out Y or N, depending on if 64K is detected.
And it works! But it is larger than the 64K ROM TO RAM code, taking up 32 bytes. I suppose there could be less typing since now the “ROM to RAM” program wouldn’t need to POKE the ‘OK’ prompt to show the user if it can be changed, so this probably is better.
0 REM 64KTEST1.BAS (32) 10 FOR L=16128 TO 16159 20 READ V:POKE L,V 30 NEXT 40 EXEC 16128 50 DATA 52,1,26,80,183,255,223 60 DATA 134,89,183,254,255,79 70 DATA 182,254,255,129,89,39 80 DATA 2,134,78,183,255,222 90 DATA 53,1,173,159,160,2,57
However … If we don’t want to be as user-friendly (printing a ‘Y’ or ‘N’), maybe some bytes could be saved by just setting a byte on the 32-column screen to indicate the result. I tried this:
start: PSHS CC Save CC ORCC #$50 Mask interrupts. STA $FFDF Disable ROM CLR $FEFF DEC $FEFF LDA $FEFF done: STA $FFDE Enable ROM PULS CC Restore CC STA $0400 RTS Return
For this routine, I save CC and disable interrupts, then disable ROM. I then clear memory location $FEFF, and then try to Decrement whatever is there. If the CLR worked, it should be 0, and a DEC would turn it in to 255. To see what happened, I load A with whatever is at $FEFF, re-enable ROM, restore CC and then store whatever I loaded in to A to the top left of the 32 column screen. If 64K is present, an orange graphics block (255) should appear in the top left of the screen. If not, whatever value in ROM at $FEFF will be stored that. On my CoCo, that is a 0, so I should see an inverted ‘@’ sign appear on a non-64K system. This isn’t perfect, since if ROM just happened to contain 255 at that location, this test would not work.
This is 25 bytes of code. Still no savings, but the BASIC program could be smaller, which is the end goal:
0 REM 64KTEST2.BAS (25) 10 FOR L=16128 TO 16152 20 READ V:POKE L,V 30 NEXT 40 EXEC 16128 50 DATA 52,1,26,80,183,255,223 60 DATA 127,254,255,122,254 70 DATA 255,182,254,255,183 80 DATA 255,222,53,1,183,4,0,57
Line 40 should really be “CLS:PRINT:EXEC 16128”, otherwise the user would have to make sure they weren’t at the end of the screen when they ran it, since the next output would scroll and overwrite whatever the program POKEd to the top left of the screen.
Is this good enough?
Do you have better ideas?
Please share your thoughts in the comments.
Until next time…
A small thing. If you’re going to push and pull CC you might as well combine the pull of cc and the rts at the end as PULS CC,PC. That saves you a byte.
Would PULS PC do the same thing as an RTS?
For this instance, is saving CC even necessary?
Yes, PULS PC is the same as RTS. Saving CC is necessary if you want to unmask the interrupts when you return to BASIC.
I suppose there would be an AND operation to restore CC as well, but I’m guessing that is more clock cycles or bytes or something.
Yep, it would be ANDCC #$AF. And you’re right. It takes more time/instructions.
“In 64K, you have memory locations 0-65535 available ($0000-$FFFF). But, the last 255 bytes ($FF00-$FFFF) are used for I/O, and as far as I know, the RAM there cannot be accessed. (Is this correct?)”
You can access the last 256 bytes of a 64K RAM machine, but not at those addresses. You have to be in RAM/ROM mode and set the SAM’s Page Switch to ‘1’. This switches the Ram address “page” at 0000-7FFF to the use the upper half of the RAM rather then the lower half, putting those usually inaccessible bytes at 7F00-7FFF. A bit tricky to accomplish since switching the page means you lose access to the “normal” 32K of RAM.
Ah, so you basically bank the top 32K to appear in the lower, where it is all accessible?