Friday, October 21, 2022

Part 2e: Follow the White Rabbit

Where I want to be for the purpose of proof of concept is for the startup banner to be displayed after typing in the startup options and get to the BASIC command prompt. It would also be nice to work out how the binary format works and test that too.

According to the manual starting up 8K ALTAIR 680 BASIC is done by jumping to $0000 from the monitor prompt.

The sequence is very similar to how I will be starting it in the ETA-3400X. First I start the Fantom II monitor in ROM using D F400 from the hex keypad, then I can use L1 to load ALTAIR 680 BASIC from tape. Once that has finished loading I then jump to the start with MON> G 0 command.

The G 0 tells me that BASIC start is from RAM address $0000. Lets see where that takes us...

;--------------------------------------------
;0000 : 0D Start: sec ;cold start entry point
;--------------------------------------------
;0001 : 76 00 F3 ror ECHO
;---------------patch------------------------ ;relocated ECHO address
0001 : 76 00 70 ror ECHO
;--------------------------------------------
0004 : 7E 18 F9 X0004: jmp INIT
0007 : 7E 03 3C X0007: jmp L033C ;BASIC warm start

Ignoring the change of address for ECHO you can see it heads off to $18F9. I assume that the jmp at $0007 is for a warm start.

Lets follow the white rabbit and go to $18F9 next...

18F9 INIT: ;Initialise BASIC
18F9 : CE FF FF ldx #$FFFF ;clear current BASIC line number ?
18FC : DF 8A stx $8A
;--------------------------------------------
;18FE : 8E 1B 1B lds #$1B1B ;place stack in RAM above BASIC
;is this temporary ?
;---------------patch------------------------ ;patch for relocated z-page subroutine
;temporary stack moved to make room for relocated Cold Start routines
18FE : 8E 1B FF lds #$1BFF ;new location for stack in RAM above BASIC
;-------------------------------------------
1901 : 9F 82 sts X0082
1903 : 7F 01 11 clr X0111 ;NUL char ?
1906 : BD 08 34 jsr L0834 ;send CR, LF out
1909 : CE 00 65 ldx #$0065
190C : DF 61 stx X0061
190E : CE 1A AA ldx #$1AAA ;'MEMORY SIZE' string -1
1911 : BD 08 79 jsr L0879 ;display message
1914 : BD 08 F9 jsr L08F9 ;prompt and get user input

I had to manually disassemble this as DASMx did a bad job of it.

Briefly, #$FFFF is stored in $8A and $8B, the the Stack Pointer is moved to $1B1B (I moved it up higher later on). This seems to be a temporary location for the Stack. The SP is saved in zero-page, and address $111 is cleared for some reason. But then we jump to $0834 where I find out is sends a <CR> and <LF> to the output. After that it saves #$0065 in address $0061 for some reason, then loads the x-register with the address pointing to memory $1AAA and that happens to be one byte below the MEMORY SIZE? string.

Now we are getting somewhere.

First I have to fix the character output routine because it's surely going to jump into the ALTAIR PROM monitor and I don't have that, and will have to patch it.

Patching ALTAIR BASIC I/O routines

MicroSoft was clever about writing their BASIC so that it could be easily modified to run on different hardware that may have a different address for it's I/O routines. In the manuals it explains how to patch those routines, and there are only three of them.

Describing where the BASIC patch points are and what they do.

This should be enough for me to find and patch those routines. Let's have a look at the OUTCH location first because I want to patch it, and see some output on the screen.

08AD BASOUTCH:
;--------------------------------------------
;08AD : BD FF 81 jsr OUTCH ;call ALTAIR 680 monitor OUTCH
;---------------patch------------------------
;*** fix to 7 bit ASCII for Fantom II OUTCH VT-100
;call patched Fantom II monitor OUTCH for VT100
08AD : BD 1A BD jsr VT100OUT
;--------------------------------------------
08B0 : 33 pulb ;recover B-reg
08B1 : 39 L08B1: rts

After I tidied up the listing you see that the original code jumps to the ALTAIR PROM OUTCH address (in the top 256 bytes of memory) with the b-register containing the character to be printed. I renamed the BASIC OUTCH routine as BASOUTCH because otherwise it gets confusing having the ALTAIR monitor using the same name as BASIC. And by coincidence the Fantom II monitor also calls their version OUTCH.

On return from the ALTAIR monitors OUTCH call, it restores the b-register at $08B0 because I assume it eats it during OUTCH. The equivalent Fantom II monitor routine uses the a-register instead of the b-register but I don't have enough room to simply copy the b-reg to the a-reg without inserting bytes and moving the whole interpreter up a few bytes. I don't want to move anything in fact because it will have the undesirable side effect of making any calls to any code that has moved stop working. And also I am manually typing this in a bit at a time, not all at once.

The only solution I have is to add a small subroutine above where BASIC loads or near the top at least, then call that and return. It looks like some of those random bytes at the top of BASIC could be repurposed for that use, so I inserted the following patch:

; VT100 output patch
1ABD VT100OUT:
1ABD : 84 7F anda #$7F ;mask to 7 bits for VT-100 clone
1ABF : 7E F8 65 jmp OUTCH ;call Fantom II monitor OUTCH, and return to calling routine

As it turns out, the a-reg and b-reg both contain the output character, so I am able to just send it through, but remember that I had an 8-bit issue in Tiny BASIC where it would display strange characters at the end of lines. Later on I also found the same issue in ALTAIR 680 BASIC. The fix was simply to mask off the 8th bit again, and to do this I had to insert an ANDA #$7F command and therefore I still had to relocate this routine.

Next I need to get the INCH routine patched and hopefully I can type in a memory size and continue.

After patching the routine it looks like this:

041F BASINCH:
;--------------------------------------------
;041F : BD FF 00 jsr INCH ;Altair 680 monitor INCH
;0422 : 17 tba ;copy char to a-reg
;---------------patch------------------------ ;patch for Fantom II monitor INCH on ETA-3400X
041F : BD F8 E1 jsr INCH ;Fantom II monitor INCH
0422 : 16 tab ;copy char to b-reg
;--------------------------------------------
0423 : 33 pulb
0424 : 84 7F anda #$7F ;mask to 7 bit ACSII input
0426 : 81 0F cmpa #$0F ;is it ctrl-O (backspace)
0428 : 26 03 bne L042D ;no
042A : 73 01 11 com X0111 ;invert NUL to #$FF or v.v.
042D : 39 L042D: rts

Just as I had to rename the OUTCH routine in BASIC, I also had to rename the INCH routine as BASINCH to save confusion using the same names in the monitors and BASIC. The original code gets the character from the ALTAIR PROM monitor's INCH routine into the b-register then copies it to the a-register at $0422 after it returns to BASIC. However the Fantom II monitor gets a character into the a-register instead, so I have to call INCH in the Fantom II monitor then copy it to the b-register to get the same result. I didn't need to mask any bits as BASIC already did that at $0424.

I have ignored the BASPOLCAT routine for the moment for a few reasons.

  • I hadn't encountered BASPOLCAT it in the disassembly so far.
  • I didn't think the Fantom II version of that subroutine did the same thing.
  • Maybe I can get away with it and ignore it altogether. Why was it used.

Next I have to add the subroutine that sends a string message to the output so I can get the MEMORY SIZE? question onto the terminal screen.

After recalling that the x-register was loaded with an address one byte below the MEMORY SIZE? message string in the following disassembly it seems logical that jsr $08F9 was responsible for sending that message to the terminal

;display message at x-reg +1 until #$00
;
0879 L0879:
0879 : BD 0F 0B jsr L0F0B ;
087C L087C:
087C : BD 10 7D jsr L107D
087F : 5C incb ;increment # chars in msg
0880 L0880:
0880 : 5A decb ;decrement # chars in msg
0881 : 27 C9 beq L084C ;if last char, then return
0883 : A6 00 ldaa $00,x ;get char back from table
0885 : 8D 0E bsr TerminalOut
0887 : 08 inx ;ptr to next char
0888 : 81 0D cmpa #$0D ;Carriage Return ?
088A : 26 F4 bne L0880 ;no, next char
088C : 8D B0 bsr L083E
088E : 20 F0 bra L0880

As I did more disassembly I found I was having to go down more rabbit holes and enter even more subroutines if I wanted to get this initial testing done. Above you can see it does a call to $0F0B then another one after that to $107D. So those had to be entered as well.

Eventually I had enough of the code entered that I could finally run my first test, including the code that calculates FREE memory and displays it.

First startup test of BASIC. It's here that I can tell that I have the 8 bit problem again because of the odd characters at the end of each output string.

I can ignore the random character problem for now as it's not causing any harm, it's quite usable and not high on the list of things to fix. I know I can fix it later just as I did for Tiny BASIC output to my clone VT100 terminal.

I did notice that the BYTES FREE was displayed with leading zero's and thought that was correct and that it might be different to the ALTAIR 8800 version slightly. I ignored that too for now as it wasn't crashing the program and the value seemed correct.

This is a pretty major milestone but not quite enough for me to see the light at the end of the tunnel. I mean all it really did was display some text out to the terminal and read some characters from the keyboard, not BASIC actually starting up to the command prompt or responding to commands.

Next I go further down the rabbit hole...

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...