Meteor Microbee Port

Replacing the TRS80 ROM Basic Calls

The cause of the "Option Not Fitted" error was pretty obvious - In part 2 I noticed a couple of calls into TRS80 BASIC to print characters to the console (0x0033) and to scan the keyboard for input (00x2B) - two functions that I hadn't got around to replacing yet.

This was the original TRS80 code:

L67EC: LD      B,00h
       LD      HL,3EE0h
       CALL    002Bh
       LD      A,0Fh
       CALL    0033h
L67F9: CALL    002Bh
       OR      A
       JR      NZ,L683A

The calls to 002B seem to be to position the cursor to screen position 0x3ee0 - the center of the screen where the user enters their name. The call to 0x002b is to scan the keyboard for a pressed key. Here's the updated Microbee code which calls similar routines in the Microbee BASIC:

L67EC: LD      B,00h
       LD      HL,vram+02E0h
L67F9: 
       push    HL
       LD      A,H
       AND     0FH
       LD      H,A
       CALL    basic_rom_setcur
       POP     HL
       CALL    basic_rom_keyscn
       JR      NZ,L683A
       XOR     A

Because the TRS80 BASIC accepts the cursor location as an actual address in video memory, whereas the Microbee expects it as an offset from the start of the screen buffer, I needed to adjust it - hence the saving of HL to the stack and masking off the top 4 bits.

Keyboard entry now worked, and my high score appeared. Well sort of: it appeared when the screen first appeared, but the game flashes any highscore that was just achieved and on the first flash it became corrupted:

meteor_port10.png

Fixing the Corrupted High Score Display

In order to track this one down, I used the same techniques as previously described - that is memory write breakpoints to locate the code writing the corrupted score to the screen and quickly tracked down this code, with one of my left over comments about a value that I couldn't determine was either an address or a value.

L4600: LD      A,D
       OR      E
       RET     Z
       LD      HL,3780h        ; address or value?
       ADD     HL,DE
       LD      BC,000Ch
       LDIR

After adding HL and DE I was left with a value pointing to some random location in the middle of the code - hence the corrupted display. Obviously the value 0x3780 was some sort of offset between the screen buffer and a buffer holding the highest score message. I knew it wasn't the high score table directly since I new where it was and it was in a different format to how it was presented on screen.

So the question was how to find where it was supposed to be copying from? I tried a few things before realizing the message that was going to be copied was my name - "brad" - I'd just entered it when I got the highest score. I could just search through memory for that string, using ubee512's debugger:

ubee512>--db-findm=0x0000,0x8000,b,0x62,0x72,0x61,0x64
0x3800 0x3972 

Which found it in two locations. I new 0x3800 was the high score table. What's at 0x3972?

ubee512>--db-dumpl=0x3972
      00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  0123456789ABCDEF
3972: 62 72 61 64 80 80 80 80 80 36 37 30 80 80 80 80 "brad.....670...."
3982: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 "................"

That's it. Looking back at my memory map revealed that address to be in one of the off-screen video buffers.

So this 0x3780 value that I couldn't decide on was simply an offset between the real video buffer and the game's off-screen video buffer. Updated the code as follows:

   LD      HL,data + offs_scrbuf1 - vram + 0x10000

Because the final value is negative the +0x10000 is to wrap it back around to a positive value so the assembler doesn't generate a warning.

I also searched through the rest of the code for 0x3780 and found one other place where it was used - so took a punt and updated that one too.

Fixing the Falling Debris Trail

In a previous screen shot I showed a falling trail of debris that was getting left behind when the player's ship exploded:

meteor_port11.png

I'd hadn't got around to fixing this yet, so though I'd tackle it next, only to realize it was already fixed. That second reference to 0x3780 was the culprit.

Unable to Start the Game from Instructions Screen

Another small issue I had noticed was that I was unable to start the game from the instructions screen. It should have been simple to find, but I set breakpoints on all the places where there had been references to the TRS80's keyboard and no breakpoints were getting hit. Odd.

So I tried setting a memory read break-point on the keyboard vector for 0x3840 - the address of the key to start the game. The breakpoint tripped at an address that corresponded to this:

L45BA: DB      3Ah     ; ':'
       DB      40h     ; '@'
       DB      38h     ; '8'
       DB      0CBh
       DB      4Fh     ; 'O'
       DB      0C8h
       DB      18h
       DB      0F2h

Obviously this was code, not data. To fix this I ran YAZD again on the original binary and added another entry point to the command line so this would be disassembled too:

yazd meteor.trs80.at4500.bin --addr:0x4500 --entry:0x4500 --entry:0x45ba --xref --lst --mwr > meteor.yazd.disasm

And now the listing showed:

L45BA: LD      A,(3840h)
       BIT     1,A
       RET     Z
       JR      L45B4

Much better and after conversion for Microbee ended up as:

L45BA: 
       LD        A,KEY_SPACE
       CALL      isKeyDown
       RET       NZ

I was curious as to how execution ended up at this address and why YAZD couldn't work it out. Searching for references to L45BA showed why:

L4621: LD      HL,L5183
       CALL    L4CE6
       LD      HL,L5088
       LD      DE,L45BA                ; <--- here's the reference to the function

       ; Referenced from 461F
       ; --- START PROC L462D ---
L462D: LD      (L4611),HL
       LD      (L4642+1),DE            ; <-- Self modifying code
       LD      (L464B+1),DE
       LD      BC,036Fh

Self modifying code! In other words it was updating the target address of a couple of CALL instructions and YAZD couldn't determine that the bytes at that address were code and not data.

Full Functional

The game was now fully functional! All that was left was sound and slowing it down to compensate for the Microbee's faster clock speed.