I spent an hour last night finalising the mapping rules to convert PETSCII to Screen Codes, and then writing the code to implement them. I guess I've spent a total of three hours on this task, although the first half of that was occupied trying to figure-out how another mapping routine I found actually worked.
It was very clever, in that it used only the input value given in the Accumulator, and no other registers or memory. By shifting and masking bits in the PETSCII value it was given, and then selectively testing for specific resultant values, it derived the mapping value and left it in .A upon exit. The problem with it was that it is very difficult to trace the logic, because at any moment in the execution path you have to be aware of whatever operations have been performed on .A up to that point, what the current value in .A is, and how that relates to the original PETSCII value. Aside from some comments asserting the mapping rules at the top of the code, none of the actual logic had any annotation, so I went through it line-by-line and added my own as I worked out what it was doing.
By the time I'd finished, I'd almost lost the will to live. Whilst I'd figured-out what the routine did, it was still pretty cryptic to look at, and worst of all I'd found a discrepancy between what the comments at the top said the results should be and what the code actually did. However, at that point I conceded defeat - I couldn't face working through the logic yet again to confirm the discrepancy, and I wasn't 100% sure I hadn't made a mistake myself and the code was actually right after all. So with a possible glitch in a 43-byte routine I'd spent 90 minutes looking at and still wasn't absolutely certain I understood properly, I decided enough was enough, and abandoned it.
I fired-up a copy of Excel and produced two simple columns with the PETSCII characters and Screen Code characters side-by-side. I then converted their decimal values to binary, and analysed the differences where one mapped to the other - which gave me a simple rule in each case for what to do to the PETSCII value to turn it into a Screen Code value. I didn't have to do this for all 256 characters, because they actually map in blocks of 32 (except for value 255) so I ended-up with 9 rules - and there were duplicates, so the actual number of distinct rules is only 6.
PETSCII Value Mapping Rule Logical Operation
0-31 No change
32-63 No change
64-95 Clear bit 6 and #%10111111
96-127 Clear bit 5 and #%11011111
128-159 No change
160-191 Set bit 7, clear bit 6 ora #%10000000, and #%10111111
192-223 Clear bit 7 and #%01111111
224-254 Clear bit 7 and #%01111111
255 Output 94 lda #94I then wrote the code in it's simplest form, just testing for specific values and mapping appropriately - which gave me a routine 57 bytes long. I then spent a few minutes combining the duplicate rules, which reduced the byte count to 49. And finally I optimised it so that it re-used rule fragments where possible, and the code dropped to 40 bytes. More importantly, that's 40 bytes of readily-understandable logic, with comments. It only manipulates .A, uses no other registers or memory, and is a simple drop-through routine which only actually changes the value in .A when it performs the mapping, rather than twiddling bits as it progresses.
;-------------------------------------------------------------------------------
; CHAR2SCREEN
; Converts PETSCII values to the appropriate Screen Code.
; Notes: Load .A with PETSCII value - converted value is returned in .A
; Uses no other registers or memory
;
;
; PETSCII Value Mapping Rule Logical Operation
; 0-31 No change
; 32-63 No change
; 64-95 Clear bit 6 and #%10111111
; 96-127 Clear bit 5 and #%11011111
; 128-159 No change
; 160-191 Set bit 7, clear bit 6 ora #%10000000, and #%10111111
; 192-223 Clear bit 7 and #%01111111
; 224-254 Clear bit 7 and #%01111111
; 255 Output 94 lda #94
char2scrn SUBROUTINE
cmp #255 ; Input = 255?
bcc .tst192 ; Less than 255, skip to next test
lda #94 ; Input = 255, reset to 94
rts ; Exit
.tst192 cmp #192 ; Input >= 192?
bcc .tst160 ; Less than 192, skip to next test
and #%01111111 ; Input >= 192, clear bit 7
rts ; Exit
.tst160 cmp #160 ; Input >= 160?
bcc .tst128 ; Less than 160, skip to next test
ora #%10000000 ; Input >= 160, so set bit 7
.clear6 and #%10111111 ; Clear bit 6
rts ; Exit
.tst128 cmp #128 ; Input >= 128?
bcc .tst96 ; Less than 128, skip to next test
rts ; Exit (leave unchanged)
.tst96 cmp #96 ; Input >= 96?
bcc .tst64 ; Less than 96, skip to next test
and #%11011111 ; Input >= 96, clear bit 5
rts ; Exit
.tst64 cmp #64 ; Input >= 64?
bcs .clear6 ; Input >= 64, so clear bit 6
rts ; Less than 64, leave unchanged
It might not be as 'clever' as the routine I found, but it's understandable, and shorter. Which is nice.

0 comments:
Post a Comment