Part 6 - Replacing the TRS80 ROM Basic Calls
This article continues on from Part 5 - Improving the Debugger and More Keyboard Work.
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:
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:
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.
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.