Thursday, October 20, 2022

Part 2c: Zero Page conflicts

The 6800 CPU has an addressing mode called 'Direct Mode'. On the 6502, which I am more familiar with, this is known as zero-page. That is a mode where the CPU can access the lower 256 bytes of memory using a shorter and faster instruction.

As an example:

0393 : 96 10     ldaa X0010 ;first char in line buffer

vs.

0621 : B6 F0 02     ldaa STRAPS ;read STRAPS from ACIA at address $F002

The first example uses 2 bytes and takes 3 clock cycles, where the second example uses 3 bytes and takes 4 clock cycles.

It might not seem like much, but consider that the second example uses 50% more space and takes 33% longer to execute. Programmers (back then) take advantage of this and MicroSoft was no different. In fact you are hard pressed to find many instructions that load or save data outside of zero-page anywhere throughout the code.

In order to de-conflict zero-page usage (that is memory from $0000 and $00FF) I created a spreadsheet with a column for Fantom II monitor use and ALTAIR 680 BASIC use. As I discovered more I would update the spreadsheet. Conflicting memory was highlighted so I can then work out how to fix it.

A screen shot showing part of the spreadsheet used to list conflicts.

In the above example the ALTAIR 680 would normally use the space between $00BF and $00DC for some executable code, and this is presents me with another problem, Why not just move it to another location in zero-page. The reason is simple, there isn't enough unused space in zero page for 30 bytes because MicroSoft used a lot of that space for ALTAIR 680 BASIC and partly for the ALTAIR PROM monitor.

Instead, why not move the Fantom II monitor variables between $00C2 and $00D2 elsewhere in zero-page. For the same reason, there isn't enough unused space but more importantly I don't want to modify the Fantom monitor's usage of zero page because I still want it to run with Tiny BASIC when that is used.

Also I can't move the Fantom II variables out of zero-page because elsewhere in the ROM those variables are accessed in Direct Mode and would mean rewriting the monitor, increasing it's size slightly and slowing it down. At all costs I am not going to modify the Fantom II monitor even if it's detrimental to ALTAIR 680 BASIC performance.

For this routine between $00BF and $00DC I ended up splitting it up into three chunks and moving part of it down to $0069, one part to $00D8 (shown highlighted in green in the left column) and another part up to $00FF which would then overlap into page-1 of memory. Also it's close enough to the other routines, that I can use relative instructions to link them together if required.

00FF : FD " " db $FD
0100 : 00 " " db $00
0101 : 00 " " db $00
0102 : 00 " " db $00
0103 : 00 " " db $00
0104 : 00 " " db $00
0105 : 00 " " db $00
0106 : 00 " " db $00
0107 : 00 " " db $00
0108 : 00 " " db $00
0109 : 00 " " db $00
010A : 00 " " db $00
010B : 00 " " db $00
010C : 00 " " db $00
010D : 80 4F " O" suba #$4F
010F : C7 " " db $C7
0110 : 52 "R" db $52

Fortunately for me the first 13 bytes into page 1 don't appear to be used in this version of the ROM and the next 4 look like junk, or maybe I haven't got to that part yet. In any case I have to move it elsewhere. Moving chunks of code above $0100 elsewhere in RAM is less of an issue because it's not going to affect the size or speed of execution as it did in zero-page.

The chunk of code at $00FF now looks like this:

;---------------patch------------------------
; patch for ETA-3400X, reclaim Baudot buffer for z-page subroutines at $BF..$D6
; required due to Fantom II monitor z-page conflicts
00FF : 7C 00 DA L00FF: inc X00DA ;increment command pointer lsb
0102 : 20 CF bra L00D3
0104 : 81 3A L0104: cmpa #$3A ;is it >= ':'
0106 : 24 08 bcc L0110 ;yes, exit
0108 : 81 20 cmpa #$20 ;is it ' ' ?
010A : 27 F3 beq L00FF ;yes, ignore, next character
010C : 80 30 suba #$30 ;convert ASCII to numerical digit
010E : 80 D0 suba #$D0 ;and back to ASCII char ? why
0110 : 39 L0110: rts
;--------------------------------------------

And moving this also meant I had to move the 4 bytes at $010D. :

010D : 80 4F  " O" suba #$4F
010F : C7  " " db $C7
0110 : 52  "R" db $52

No idea what these 4 bytes were for initially. I didn't figure that out until very late in the disassembly process. Eventually I worked out it was part of the random number generator subroutine data.

;--------------------------------------------    ;moved to $1AB8
;010D : 80 4F C7 52 db $80, $4F, $C7, $52 ;=0.81163514 RND() seed
;--------------------------------------------

I'll explain the binary format shown above later.

By moving these data and subroutines around in zero-page meant I had to patch ALL of the addresses throughout the ALTAIR 680 BASIC ROM, and there were hundreds of them. This section of zero-page was by far the hardest part of the whole porting process. In fact I had to move stuff around in zero-page a couple of times and that meant I had to go through the entire ROM that I had entered up to that point and patch a lot of addresses all over again, as if it was some kind of sadistic game.

Why manual disassembly ?

This leads me to why I ended up just manually disassembling a lot of the code from the initial DASMx output listing.

DASMx can only do it's best guess at what is happening in the code. When you have program instructions mixed in with data section and then throw in some self modifying code or self modifying data then DASMx attempts to disassemble it as program code or data, but it's just a best-guess scenario. So for the RND subroutine data, part of it disassembles as code (suba #$4F), and part is disassembled as data (db $C7, db $52). In reality its data, but that takes time to work out. If I ran into sections that looked like data then I left that for Ron! (lateR-on).

What is a Skip Trick ?

Another trick that MicroSoft used extensively was commonly known as a skip trick. That is where a normal instruction is used as it appeared in memory, but it was also accessed from elsewhere in memory by jumping into the middle of the instruction.

An example of this is show below in the AND and OR subroutine:

0B95 : 86 4F " O" ldaa #$4F
0B97 : 97 5A " Z" staa X005A
0B99 : BD 0C D8 "   " jsr L0CD8

This is what the routine looks like straight out of DASMx. Looks fine at first glance.

However elsewhere in the code there is a jump to address $0B96!!! in the middle of the first instruction (ldaa #$4F) What?, surely that's a mistake.

here it's part of a table of jump addresses:

0156 : 50 0B 96 db $50, DW FnAnd ;AND

After checking that I didn't have a typo, I have to work out why it's jumping to the middle of an instruction.

;*******************************************************************
; Perform OR or AND function
0B95 : 86 FnOr db $86         ;= ldaa #$4F : setup #$4F (OR) or #$00 (AND) trick
0B96 : 4F FnAnd db $4F         ;= clra
0B97 : 97 5A staa X005A
0B99 : BD 0C D8 jsr L0CD8 ;evaluate integer expression (no sign check) ?

If you disassemble $86 $4F you do get ldaa #$4F (two bytes), which loads accumulator A with #$4F, but if you disassemble the byte starting at $0B96 instead you get clra (one byte) which means the accumulator A will be loaded with #$00.

On exit at $0B97, accumulator A will be either #$4F or #$00 depending on where you entered this subroutine. That result is then stored in address $005A for use later on. I figure out later it's just used as a flag to indicate OR or AND function is being performed.

Here's another example:

144E : CE 00 A3 L144E: ldx #$00A3
1451 : 8C db $8C ;cpx #$DE9E skip trick ?
1452 : DE 9E L1452: ldx $009E ;FOR/NEXT ptr ?
1454 : 96 AF L1454: ldaa X00AF ;get no of chars in msg

At $144E the x-reg is loaded with #$00A3, then it's followed by $8C, $DE, $9E which is actually a valid 3 byte instruction that disassembles as cpx #$DE9E. It then falls through to $1454: ldda $00AF. Why are we loading the x-reg with #$DE9E. Well I have no idea yet, however elsewhere in the ROM there is this:

09DB : BD 14 52 jsr L1452

This causes program to jump into the middle of the cpx #$DE9E instruction, and instead it loads the x-register with the contents of $009E in a 2-byte instruction.

All these tricks are to save 1 or 2 bytes and the ROM is littered with them. I've found about 15 of them so far, but there is probably more as I haven't disassembled any of the math routines.

Next I scan through the ROM and look for clues...


No comments:

Post a Comment

Part 2o: The future of the ETA-3400X

You can find all the work I've done on the ETA-3400X and 8K ALTAIR 680 BASIC, except for the ETA-3400X Gerber files, on the ET-3400 grou...