Theory of Operation - Software

 

New Program Code

Adding the hardware is one thing but to support the EEPROM there needs to be a some driver routines that talk using the ''microwire' to the  93c56 device. One thing about the serial EEPROM is that it's more complex to talk to than a parallel memory. Traditional parallel memory is simple to deal with, once you have the hardware in place the CPU just reads and writes to it. With the serial device you need to write some routines to send data serially, 1 bit at a time to the device. The device also needs to be sent commands, to tell it what you want to do, eg. write a byte or read a byte. 

Fortunately as a base I had some code I'd already written, but not for z80,  it simply required some porting to Z80 assembler. 

Full details of how the EEPROM works, the commands it uses are all contained in the Datasheet for the device. See the main page for a link to the Microchip version of the 93C56.

Size Constraints

The new code and routines had very limited space to fit in, so the code was written with the aim of keeping things as tight as possible. The serial routines fitted with only a byte to spare from $3F30 -> $3FA0 and at the rest of the routines $3565, both these area's do not interfere with the game code. 

Some of the code might appear a little strange but small code size was the most important factor

Patches

All this new code doesn't do a great deal of good without patching the original game to make use of it, this is where some detective work comes into play. Basically it involves disassembling the game code and working out what routines do what. The source code below details all the patch points for the game ROMs where routines have been modified to call the new code.

 

Full Source + Patch Comments

The source was compiled using "TASM Z80 Assembler. Version 3.1 February, 1998.
Copyright (C) 1998 Squak Valley Software"

Features Added

; -----------------------------------------------------------------
; Donkey Kong High-Score save & Serial EEPROM driver
; -----------------------------------------------------------------
;
; - Version 1.0
; - Another JROK production
; - 31st JAN 2001
;
; Note: this code has been written purely to keep size to a
; minimum, so some of it may appear a little bizzare but it
; saves bytes.. trust me on that ;-)
;
; - code designed for ST 93C56A / 93C66A - serial EEPROM
; - which supports 8bit read/write *** MUST be a 256x8 device ***
; - 512x8 ( 93c66 also works )
;
; Assembled using "TASM z80 Assembler. Version 3.1"
;
; -----------------------------------------------------------------
; Code is divided into two part
;
; part 1 - $3565 - Highscore functions, save/restore/reset etc.
;
; part 2 - $3F30 - Serial EEPROM driver routines, read/write
;
; -----------------------------------------------------------------
; Control Addresses
;
; Serial Data Read Port = bit 7 address $7C00
; Output latch 6H - Q7 = 7d06 = clock
; Output Latch 6H - Q6 = 7d07 = data
; Output Latch 5H - Q1 = 7d81 = Chip Select
;
; Output latches only use bit 0 for output bit
;
;
;
; -----------------------------------------------------------------
; Game Program Patches
; -----------------------------------------------------------------
;
;
; -----------------------------------------------------------------
; ROM DK.5E loaded ($0000-$0fff)
; -----------------------------------------------------------------
;
; $1C6 - sets on screen "high score"
; - patch to call 'restore high-scores' @$25B
; - jp $025B
;
; $1CA - start location of copy changed ( $1B2 -> $1b8 )
; $1CD - length of copy changed ( 9 bytes -> 3 bytes )
;
; $25A - old initialize HS Table now just a return ( $C9 - ret )
; $25b - new call to restore high-scores and set on-screen HS


; @25b call 3565h ; call restore or initialize
; ld hl, 611Dh ; < set on screen to 1st high
; ; score entry
; jp loc_0_1C9
;
; -----------------------------------------------------------------
; ROM DK.5C loaded ($1000-$1fff)
; -----------------------------------------------------------------
;
; $14A6: 1E 3C <- high-score time ( 60 seconds )
; $1557: 88 68 <- high-score data end of entry area
; $1558: 75 74 for 12 digits
;
;
; $15F3 - calls save_inits (ROM 5B) - jp $3593 
;
;
;
;
; -----------------------------------------------------------------
; ROM DK.5A loaded ($3000-$3fff)
; -----------------------------------------------------------------
;
; $3565 - Save Scores Code
;
; $373D to $3745 2D <- underscore characters
;
; $3F30 - EEPROM routines
;
;

;
;
; -----------------------------------------------------------------
;
;
;
;
;

INo .EQU $7C00

; 7d06 = clock
; 7d07 = data
; 7d81 = Select

.DEFINE cs_active ld ( hl ), l ; enable EEPROM 
.DEFINE cs_inactive ld ( hl ), 0 ; disable EEPROM


.org $3565




; always enable the write to the EEPROM

call enable_write 


; check for 1p & 2p start + 1p JUMP held down
; if all held down then reset the high-score table


ld a, ( $7d00 ) 
and $0C
ld c,a
ld a, ( $7c00 )
and $10
or c
cp $1C
jr z, RESET_HST



; set read mode and read in the HST
ld c,0
call READ_WRITE_HST 


; see if the first byte is $94 ( as it should be )

xor a
ld b,a
call get_a_byte
cp $94
jr nz, RESET_HST




call CALC_CS ; checksum it
ld c,a
ld b, $AA ; address $AA = the checksum byte
call get_a_byte 
cp c
ret z ; checksum's is ok.

jr RESET_HST



; -----------------------------------------------------------------
SAVE_INITS: 
; -----------------------------------------------------------------

; this routine is called FROM #15F3

call SAVE_HS_TABLE
ld de, $31a ; Name has been registered

ret

; -----------------------------------------------------------------
CALC_CS:
; -----------------------------------------------------------------
; simple 8bit checksum

ld b, $AA
ld hl, $6100 ; $6100-$60A9 checksum
xor a


ccs_lop:
add a,(hl)
inc hl
djnz ccs_lop
ret


; -----------------------------------------------------------------
RESET_HST:
; -----------------------------------------------------------------


; create a new high-score table

ld hl, $6100
ld a, 1
ld c, $94
ld e, $F4



chr_next_entry:


ld ( hl ), c
inc hl
ld ( hl ), $77 ; screen address
inc hl

ld ( hl ),a ; 1st
inc hl

ld b, 4 ; spaces
cht_l0: ld ( hl ) , $10
inc hl
djnz cht_l0
; score digits
ld b, 6
cht_l1: ld ( hl ) ,0 
inc hl
djnz cht_l1

ld b, 14 ; NAME
cht_l2:
ld (hl), $10
inc hl
djnz cht_l2

ld ( hl ), $3F ; end of text char
inc hl

ld b, 4
cht_l3: ld ( hl ) , 0 ; score value
inc hl
djnz cht_l3


ld ( hl ), e ; end address
inc hl
ld ( hl ), $76
inc hl


inc c
inc c
inc e
inc e


inc a
cp 6
jr nz, chr_next_entry


; now save the HST


; -----------------------------------------------------------------
SAVE_HS_TABLE:
; -----------------------------------------------------------------


ld c, 1 ; read/write

; -----------------------------------------------------------------
READ_WRITE_HST:
; -----------------------------------------------------------------

;c = 0 read
;c = 1 write


ld de, $6100

rw_hst_lop0:
ld b,e
ld a,c
and a
jr z, dord

dowr:
ld a, ( de )
call write_a_byte

jr cntn

dord:
call get_a_byte
ld ( de ),a

cntn:

inc de
ld a,e
cp $aa ; address 0 to $AA
jr nz, rw_hst_lop0



ld a,c
and a ; if reading just end
ret z ; 


; write mode so calc and save checksum

call CALC_CS ; calc and write HS
ld b, $AA
call write_a_byte

ret



; ***********************************************
; *
; * Second section of code !!
; *
; * The EEPROM read/write routines !!
; *
; * This needed to fit between $3F30 and $3f9F
; *
; ***********************************************

.org $3f30



; -----------------------------------------------------------------
write_a_byte:
; -----------------------------------------------------------------

; b = address (0-255)
; a = value to write



push af
ld a, $40 ; write op-code
call send_op_and_address
pop af
wb_sb:
call send_8_bits
cs_inactive ; signal 'start' write

jr wait_end



; -----------------------------------------------------------------
get_a_byte:
; -----------------------------------------------------------------
; b = address to read 
; a = value read



ld a, $80 ; read op-code
call send_op_and_address
push bc

ld b,8 

gb_lop0: ; 1st bit of read is a dummy bit
call do_clock 
ld a, ( INo )
rlca
rl c
djnz gb_lop0

ld a,c
xor 255 ; data is inverted by the LS240
; so it must be inverted to get
; the correct byte

pop bc
jr we_end



; -----------------------------------------------------------------
send_op_and_address:
; -----------------------------------------------------------------

call send_op_code
call start_bit ; set a8 high ( only for 93c66)
ld a,b ; 8bit address


; -----------------------------------------------------------------
send_8_bits:
; -----------------------------------------------------------------
; a = byte to send
ld b, 8
sb_lop1:
rlca ; rotate a and clock the
ld ( ix + 01 ), a ; bits out
call do_clock
djnz sb_lop1
ret


; -----------------------------------------------------------------
enable_write:
; -----------------------------------------------------------------

xor a ; 00 11xxxxxxx enable write
call send_op_code ; opcode 
dec a ; x = don't care
;
ld b,9 ; clock out 9 bits
call sb_lop1
jr we_end



; -----------------------------------------------------------------
start_bit:
; -----------------------------------------------------------------
; only bit0 of writing to the control latches
; is used so 'l' is used to set any of the
; latches to 1 ( as bit 0 of 'l' = 1 )

ld ( ix + 01 ), l ; data high

; -----------------------------------------------------------------
do_clock:
; -----------------------------------------------------------------


ld ( ix + 00 ), l ; clock high
ld ( ix + 00 ), 0 ; clock low
ret

; -----------------------------------------------------------------
send_op_code:
; -----------------------------------------------------------------



ld hl, $7d81 ; Select
ld ix, $7d06 ; Clk/Data index


cs_active ; enable eeprom
call start_bit ; A reg. bit 7 & 6 = opcode 
call send_opc ; write the op code

send_opc:

rlca ; this gets executed twice
ld ( ix + 01 ), a ; writing the two bits of
jr do_clock ; opcode to the EEPROM





; -----------------------------------------------------------------
wait_end:
; -----------------------------------------------------------------

; during a write the EEPROM pulls dataout
; low, ( it's inverted by the LS240 ) so you
; have to wait for bit 7 to go low
; for write to have completed



cs_active

; when Q = low ( data IN0 =high )
; Q = high ( data IN0 =low )

we_wait:

ld a, ( INo ) ; bit7 = (0=1) (1=0) 1= EEPROM write busy
rlca
jr c, we_wait ; loop while 1

we_end:
cs_inactive ; disable EEPROM
ret

.end