Part 4 - Keyboard and Flashing Text
This article continues on from Part 3 - Development Environment and Title Screen.
Keyboard support is quite different between TRS80 and Microbee. On TRS80, it's a range of memory addresses with one bit in each byte representing one particular key. This extract from the book TRS80 Assembly Language Programming shows the arrangement:
The Microbee on the other hand relies on a software routine to pass the key code of interest to the the 6545 and then waits for a response indicating whether the key is pressed.
I located the following routine when I was working on fixing up the Galaxy Invasion port, so I just grabbed a copy. (I think this routine may have originated from the BASIC ROM, but not sure). See also Microbee Keyboard Scancodes.
; --- isKeyDown --- ; IN: A = Key Scan Code to Check (not ascii code) ; OUT: Z set if key is pressed ; For a table of scan codes, see http://fpgabee.toptensoftware.com/microbee/KeyboardScanCodes isKeyDown: PUSH BC LD C,A LD B,A LD A,12h OUT (0Ch),A LD A,B RRCA RRCA RRCA RRCA AND 03h OUT (0Dh),A LD A,13h OUT (0Ch),A LD A,B RLCA RLCA RLCA RLCA OUT (0Dh),A LD A,01h OUT (0Bh),A LD A,10h OUT (0Ch),A IN A,(0Dh) LD A,1Fh OUT (0Ch),A OUT (0Dh),A L095D: IN A,(0Ch) BIT 7,A JR Z,L095D IN A,(0Ch) CPL BIT 6,A LD A,00h OUT (0Bh),A LD A,C POP BC RET
With this routine in place, the next step was to locate all the references to 38xxH and replace the tests with calls to
isKeyDown. So code like this:
LD A,(3840h) ; Key scan memory address BIT 0,A JP NZ,L4521 ; Enter Key (Instructions) BIT 1,A RET Z ; Clear Key (Start Game) LD SP,stacktop JP L4531 ; Jump to start of game
LD A,55 ; Space key CALL isKeyDown JP Z,L_start LD A,52 ; Enter key CALL isKeyDown JP Z,L4521 RET L_start: LD SP,stacktop JP L4531
That's a fairly simple example. Some of the other areas involved some convoluted bit manipulation so for the moment I just disabled it and I'll sort it out later. There was also some IN (0x13) - I don't know what that's for, but guessing joystick support. Again, for the moment I've just disabled that code by commenting it out.
The other thing I did here was change the instructions from "<CLEAR>" to "<ENTER>".
Once I had the Enter Key and the '1' an '2' keys working, I had enough to press Enter and enter a number of players. The game started, but - well, it's not supposed to look like this:
Those trails running down the screen are the exploded ship particles not getting cleared away.
Incorrectly Flashing Score
Confronted with the above I decided to attack it from the simplest issue and work up, the theory being that progressively better understanding of the code will help with the harder problems.
The simplest problem I could spot was the player's score was flashing between "0" and "-", instead of just flashing. I started by trying to locate the code that prints the score and came across it pretty quickly (it's almost the first thing that's done when the game starts).
A bit of reverse engineering of the code and it was soon obvious that there was an address offset that was used to offset that value drawn from the player's score to an array of space characters. A couple of labels replaces this:
LD DE,0EB0Ch ; Literal offset between two locations in memory
to do the math instead:
LD DE,L668E - offs_p1_score
The same fix for player 2's score and one problem was sorted.
Time for some Serious Debugging
Next I tried a number of different approaches to get to the bottom of the messed up graphics. I wont detail it all here because it was all a bit haphazard, but I tried things like just reviewing for mistakes and various debugging approaches with ubee512's debugger.
Finally I decided to just let it run with tracing enabled and watch for weird addresses in any of the registers. It only took a few minutes before I saw the HL register with a value in the 0x6000 range (which would have been a valid address at the original program location, but not any more). A few more runs and some quick breaking and I managed to locate where the value first came from:
LD L,(IY+00h) LD H,(IY+01h)
Looking at the value of IY and using the z80asm's listing file to convert the address back to a location in code led me to this:
DB 0B7h DB 6Fh ; 'o' DB 83h DB 18h
So 0b7h and 06fh is actually another hard coded address of 0x6fb7.
Looking around this location it became apparent that there were alot of addresses like this so I returned to the code to try and understand it a little better. More backtracking and I finally found this:
L5FA8: SLA A SLA A LD E,A LD D,00h LD IY,L522F ADD IY,DE
ie: IY is loaded with L522F + A * 4, or every second word from that address onwards is a pointer that needs to be updated to a label, as shown below. Obviously the fourth one is unused and someone had a sense of humour.
L522F: DW L702F DB 42h ; 'B' DB 08h DW L6FFF DB 42h ; 'B' DB 08h DW L6FCF DB 42h ; 'B' DB 08h DB 53h ; 'S' :) DB 48h ; 'H' DB 49h ; 'I' DB 54h ; 'T' DW L6D35 DB 32h ; '2' DB 06h DW L6D59 DB 32h ; '2' DB 06h ; Etc... for many more entries
By way of explanation, it looks like these addresses are references to bitmap image data and explains why the meteors looked all wrong.
Looking much better now (but still not perfect):
A Note On List Files and Memory Addresses
In doing all this work, it's often necessary to convert from physical addresses (ie: after relocation) to source code locations (and vice versa).
The easiest way to do this is with z80asm's --list option that writes out the physical locations.
For example if I wanted to set a break point on function L453D I'd need to look it up in the listing file, where I'd see its physical location is 0x0943.
0943 ; Referenced from 459C 0943 ; --- START PROC L453D --- 0943 3a 04 38 L453D: LD A,(data+04h) 0946 fe 20 CP 20h ; ' ' 0948 20 05 JR NZ,L4549 094a 11 c8 38 LD DE,data+00C8h 094d 18 03 JR L454C
The listing file can also be used to translate physical address discovered in the debugger back to source code locations.
Another thing to note, is that it's helpful to have the program relocated to far away from it's original location as it makes it obvious which address space a location belongs to and helps reduce confusion.
So it's getting there! There's still a few issue with the current display - notably the corrupts score indicators on the score pad and the weird bounding flagship in the top left corner.