Porting Meteor Mission II to Microbee

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:

trs80_keys.png

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

becomes:

	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:

meteor_port_5.png

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):

meteor_port_6.png

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.

Getting There

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.

Porting Meteor Mission II to Microbee continues with Part 5 - Improving the Debugger and More Keyboard Work.