Last active
November 29, 2015 17:53
-
-
Save BlockoS/ad7098771aea4970886b to your computer and use it in GitHub Desktop.
Memory base 128 routines (extracted from Shin Megami Tensei and Tadaima Yuusha Boshuuchuu)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.include "system.inc" | |
.include "startup.asm" | |
.include "mb128.asm" | |
.include "font.inc" | |
main: | |
sei | |
; Upload font. | |
lda #low(font_8x8) | |
sta <_si | |
lda #high(font_8x8) | |
sta <_si+1 | |
st0 #$05 | |
st1 #%0000_0000 | |
st2 #%0000_0000 | |
st0 #$00 | |
st2 #low($1000) | |
st2 #high($1000) | |
lda #FONT_8X8_COUNT | |
sta <_al | |
st0 #$02 | |
.l0: | |
ldx #$08 | |
cly | |
.l1: | |
lda [_si], Y | |
sta video_data_l | |
st2 #$00 | |
iny | |
dex | |
bne .l1 | |
st1 #$00 | |
st2 #$00 | |
st2 #$00 | |
st2 #$00 | |
st2 #$00 | |
st2 #$00 | |
st2 #$00 | |
st2 #$00 | |
st2 #$00 | |
clc | |
lda <_si | |
adc #$08 | |
sta <_si | |
bcc .l2 | |
inc <_si+1 | |
.l2: | |
dec <_al | |
bne .l0 | |
; Set palette | |
stz color_reg_l | |
stz color_reg_h | |
stz color_data_l | |
stz color_data_h | |
lda #$ff | |
sta color_data_l | |
lda #$01 | |
sta color_data_h | |
; Scroll | |
st0 #$07 | |
st1 #$00 | |
st2 #$00 | |
st0 #$08 | |
st1 #$00 | |
st2 #$00 | |
; Setup BAT (32x32) | |
st0 #$09 | |
st1 #$00 | |
st2 #$00 | |
; Memory base 128 stuffs | |
jsr mb128_boot | |
lda #low($2200) | |
sta <_di | |
lda #high($2200) | |
sta <_di+1 | |
jsr mb128_read_header | |
; Display result | |
ldx #low($2200) | |
stx <_di | |
ldx #high($2200) | |
stx <_di+1 | |
st0 #$05 | |
st1 #%1_000_0000 | |
st2 #$00 | |
st0 #$00 | |
st1 #$00 | |
st2 #$00 | |
ldx #$02 | |
.start: | |
cly | |
st0 #$02 | |
.display: | |
lda [_di], Y | |
lsr A | |
lsr A | |
lsr A | |
lsr A | |
cmp #$0a | |
bcc .l3 | |
clc | |
adc #$17 | |
bra .l4 | |
.l3: | |
adc #$10 | |
.l4: | |
sta video_data_l | |
lda #$01 | |
adc #$00 | |
sta video_data_h | |
lda [_di], Y | |
and #$0f | |
cmp #$0a | |
bcc .l5 | |
clc | |
adc #$17 | |
bra .l6 | |
.l5: | |
adc #$10 | |
.l6: | |
sta video_data_l | |
lda #$01 | |
adc #$00 | |
sta video_data_h | |
iny | |
bne .display | |
inc <_di+1 | |
dex | |
bne .start | |
.loop: | |
bra .loop |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;--------------------------------------------------------------------- | |
; Constants. | |
;;--------------------------------------------------------------------- | |
; Number of bytes per sector. | |
MB128_SECTOR_SIZE=$200 | |
; Maximum number of sectors. | |
MB128_SECTOR_COUNT=$100 | |
; Number of bytes per entry. | |
MB128_ENTRY_SIZE=$10 | |
; Maximum number of entries. | |
MB128_ENTRY_COUNT=$40 | |
; Detection retry count at boot (0 = 256) | |
MB128_BOOT_RETRY_COUNT=8 | |
;;--------------------------------------------------------------------- | |
; Default header. | |
;;--------------------------------------------------------------------- | |
mb128_default_header: | |
.db $30,$06,$00,$00 | |
;;--------------------------------------------------------------------- | |
; Header string. | |
;;--------------------------------------------------------------------- | |
mb128_string: | |
.db $d2,$d3,$d8,$cd,$de,$b0,$bd,$31,$32,$38,$00,$00 | |
;;--------------------------------------------------------------------- | |
; Entry offsets. | |
;;--------------------------------------------------------------------- | |
mb128_entry_off.lo: | |
.dwl $0000, $0010, $0020, $0030, $0040, $0050, $0060, $0070 | |
.dwl $0080, $0090, $00A0, $00B0, $00C0, $00D0, $00E0, $00F0 | |
.dwl $0100, $0110, $0120, $0130, $0140, $0150, $0160, $0170 | |
.dwl $0180, $0190, $01A0, $01B0, $01C0, $01D0, $01E0, $01F0 | |
.dwl $0200, $0210, $0220, $0230, $0240, $0250, $0260, $0270 | |
.dwl $0280, $0290, $02A0, $02B0, $02C0, $02D0, $02E0, $02F0 | |
.dwl $0300, $0310, $0320, $0330, $0340, $0350, $0360, $0370 | |
.dwl $0380, $0390, $03A0, $03B0, $03C0, $03D0, $03E0, $03F0 | |
mb128_entry_off.hi: | |
.dwh $0000, $0010, $0020, $0030, $0040, $0050, $0060, $0070 | |
.dwh $0080, $0090, $00A0, $00B0, $00C0, $00D0, $00E0, $00F0 | |
.dwh $0100, $0110, $0120, $0130, $0140, $0150, $0160, $0170 | |
.dwh $0180, $0190, $01A0, $01B0, $01C0, $01D0, $01E0, $01F0 | |
.dwh $0200, $0210, $0220, $0230, $0240, $0250, $0260, $0270 | |
.dwh $0280, $0290, $02A0, $02B0, $02C0, $02D0, $02E0, $02F0 | |
.dwh $0300, $0310, $0320, $0330, $0340, $0350, $0360, $0370 | |
.dwh $0380, $0390, $03A0, $03B0, $03C0, $03D0, $03E0, $03F0 | |
;;--------------------------------------------------------------------- | |
; Error values. | |
;;--------------------------------------------------------------------- | |
; [todo] | |
;;--------------------------------------------------------------------- | |
; Write a single bit to memory base 128. | |
; in : A Bit to send. | |
; out : | |
;;--------------------------------------------------------------------- | |
mb128_write_bit: | |
and #$01 | |
sta joyport | |
pha | |
pla | |
nop | |
ora #$02 | |
sta joyport | |
pha | |
pla | |
pha | |
pla | |
pha | |
pla | |
and #$01 | |
sta joyport | |
pha | |
pla | |
nop | |
rts | |
;;--------------------------------------------------------------------- | |
; Write a single byte to memory base 128. | |
; in : A Byte to send. | |
; out : | |
; use : _al | |
;;--------------------------------------------------------------------- | |
mb128_write_byte: | |
phx | |
ldx #$08 | |
sta <_al | |
.loop: | |
lsr <_al | |
cla | |
rol A | |
sta joyport | |
pha | |
pla | |
nop | |
ora #$02 | |
sta joyport | |
pha | |
pla | |
pha | |
pla | |
pha | |
pla | |
and #$01 | |
sta joyport | |
pha | |
pla | |
nop | |
dex | |
bne .loop | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Read a single bit from memory base 128. | |
; in : | |
; out : A bit read. | |
;;--------------------------------------------------------------------- | |
mb128_read_bit: | |
stz joyport | |
pha | |
pla | |
nop | |
lda #$02 | |
sta joyport | |
pha | |
pla | |
nop | |
lda joyport | |
stz joyport | |
pha | |
pla | |
and #$01 | |
rts | |
;;--------------------------------------------------------------------- | |
; Read a single byte from memory base 128. | |
; in : | |
; out : A Byte read. | |
; use : _al | |
;;--------------------------------------------------------------------- | |
mb128_read_byte: | |
phx | |
stz <_al | |
ldx #$08 | |
.loop: | |
stz joyport | |
pha | |
pla | |
nop | |
lda #$02 | |
sta joyport | |
pha | |
pla | |
nop | |
lda joyport | |
lsr A | |
ror <_al | |
sta joyport | |
pha | |
pla | |
nop | |
dex | |
bne .loop | |
plx | |
lda <_al | |
rts | |
;;--------------------------------------------------------------------- | |
; Detect if a memory base 128 is present. | |
; in : | |
; out : A $ff if detection failed, $00 if a memory base 128 was | |
; succesfully detected. | |
; use : _dl, _al | |
;;--------------------------------------------------------------------- | |
mb128_detect: | |
phx | |
ldx #$03 | |
.loop: | |
lda #$a8 | |
jsr mb128_write_byte | |
cla | |
jsr mb128_write_bit | |
lda joyport | |
asl A | |
asl A | |
asl A | |
asl A | |
sta <_dl | |
lda #$01 | |
jsr mb128_write_bit | |
lda joyport | |
and #$0f | |
ora <_dl | |
cmp #$04 | |
beq .found | |
dex | |
bne .loop | |
.not_found: | |
lda #$ff | |
plx | |
rts | |
.found: | |
cla | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Detect and reset Memory Base 128 states. | |
; in : | |
; out : A $ff if detection failed, $00 if a memory base 128 was | |
; succesfully detected. | |
; use : _al, _dl | |
;;--------------------------------------------------------------------- | |
mb128_boot: | |
phx | |
phy | |
ldx #MB128_BOOT_RETRY_COUNT | |
.retry: | |
jsr mb128_detect | |
cmp #$00 | |
beq .init | |
dex | |
bne .retry | |
.fail: | |
lda #$ff | |
ply | |
plx | |
rts | |
.init: | |
lda #$01 | |
jsr mb128_write_bit | |
cla | |
jsr mb128_write_bit | |
jsr mb128_write_bit | |
jsr mb128_write_byte | |
lda #$01 | |
jsr mb128_write_byte | |
cla | |
jsr mb128_write_byte | |
cla | |
jsr mb128_write_bit | |
jsr mb128_write_bit | |
jsr mb128_write_bit | |
jsr mb128_write_bit | |
jsr mb128_read_bit | |
cla | |
jsr mb128_write_bit | |
jsr mb128_write_bit | |
jsr mb128_write_bit | |
.success: | |
cla | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Set internal memory base 128 address to the specified sector. | |
; in : _bl Sector number | |
; out : | |
; use : _al | |
;;--------------------------------------------------------------------- | |
mb128_sector_addr: | |
lda #$01 | |
jsr mb128_write_bit | |
cla | |
jsr mb128_write_bit | |
jsr mb128_write_bit | |
lda <_bl | |
jsr mb128_write_byte | |
cla | |
jsr mb128_write_byte | |
lda #$10 | |
jsr mb128_write_byte | |
cla | |
jsr mb128_write_bit | |
jsr mb128_write_bit | |
jsr mb128_write_bit | |
jsr mb128_write_bit | |
rts | |
;;--------------------------------------------------------------------- | |
; Write data from source buffer to the specified memory base 128 | |
; sectors. | |
; in : _bl Sector number | |
; _bh Sector count | |
; _si Source pointer | |
; out : _cx CRC | |
; A $ff if an error occured, $00 upon success. | |
; use : _al, _dl | |
;;--------------------------------------------------------------------- | |
mb128_write_sectors: | |
phx | |
phy | |
stz <_cx | |
stz <_cx+1 | |
.write_next_sector: | |
; Write a sector | |
jsr mb128_detect | |
cmp #$00 | |
bne .error | |
jsr mb128_sector_addr | |
ldx #$02 | |
.write_next_256: | |
cly | |
.write_next_byte: | |
lda [_si], Y | |
jsr mb128_write_byte | |
iny | |
bne .write_next_byte | |
inc <_si+1 | |
dex | |
bne .write_next_256 | |
; Rewind source buffer | |
dec <_si+1 | |
dec <_si+1 | |
; Compare read data with source | |
jsr mb128_detect | |
cmp #$00 | |
bne .error | |
jsr mb128_sector_addr | |
ldx #$02 | |
.check_next_256: | |
cly | |
.check_next_byte: | |
jsr mb128_read_byte | |
cmp [_si], Y | |
bne .error | |
clc | |
adc <_cl | |
sta <_cl | |
bcc .inc0 | |
inc <_ch | |
.inc0: | |
iny | |
bne .check_next_byte | |
inc <_si+1 | |
dex | |
bne .check_next_256 | |
inc <_bl | |
dec <_bh | |
bne .write_next_sector | |
.ok: | |
cla | |
ply | |
plx | |
rts | |
.error: | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Fill specified memory base 128 sectors with 0 (untested). | |
; in : _bl Sector id | |
; _bh Sector count | |
; out : A $ff if an error occured, $00 upon success. | |
; use : _al, _dl | |
;;--------------------------------------------------------------------- | |
mb128_clear_sectors: | |
phx | |
phy | |
.clear_next_sector: | |
; Write a sector | |
jsr mb128_detect | |
cmp #$00 | |
bne .error | |
jsr mb128_sector_addr | |
ldx #$02 | |
.clear_next_256: | |
cly | |
.clear_next_byte: | |
cla | |
jsr mb128_write_byte | |
iny | |
bne .clear_next_byte | |
inc <_si+1 | |
dex | |
bne .clear_next_256 | |
; Check data | |
jsr mb128_detect | |
cmp #$00 | |
bne .error | |
jsr mb128_sector_addr | |
ldx #$02 | |
.check_next_256: | |
cly | |
.check_next_byte: | |
jsr mb128_read_byte | |
cmp #$00 | |
bne .error | |
iny | |
bne .check_next_byte | |
inc <_si+1 | |
dex | |
bne .check_next_256 | |
inc <_bl | |
dec <_bh | |
bne .clear_next_sector | |
.ok: | |
cla | |
ply | |
plx | |
rts | |
.error: | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Read data from specified memory base 128 sectors and store it to the | |
; destination buffer. | |
; in : _bl Sector id | |
; _bh Sector count | |
; _di Destination pointer | |
; out : _cx CRC | |
; A $ff if an error occured, $00 upon success. | |
; use : _al, _dl | |
;;--------------------------------------------------------------------- | |
mb128_read_sectors: | |
phx | |
phy | |
stz <_cl | |
stz <_ch | |
.read_next_sector: | |
jsr mb128_detect | |
cmp #$00 | |
bne .error | |
jsr mb128_sector_addr | |
ldx #$02 | |
.read_next_256: | |
cly | |
.read_next_byte: | |
jsr mb128_read_byte | |
sta [_di], Y | |
; CRC is just the sum of all bytes | |
clc | |
adc <_cl | |
sta <_cl | |
bcc .l0 | |
inc <_ch | |
.l0: | |
iny | |
bne .read_next_byte | |
inc <_di+1 | |
dex | |
bne .read_next_256 | |
inc <_bl | |
dec <_bh | |
bne .read_next_sector | |
.ok: | |
cla | |
ply | |
plx | |
rts | |
.error: | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Read memory base 128 first sector header. | |
; in : _di Pointer to destination buffer containing the first sector. | |
; out : A $ff if an error occured. $00 upon success. | |
; use : _ax, _bx, _cx, _dl, _si | |
;;--------------------------------------------------------------------- | |
mb128_read_header: | |
phx | |
phy | |
stz <_bl | |
lda #$02 | |
sta <_bh | |
jsr mb128_read_sectors | |
cmp #$00 | |
bne .error | |
; The first 2 bytes of the sectors is the sum of all sector bytes | |
; minus the first 2 bytes. | |
; In order to perform a valid check we must first substract them | |
; from the CRC computed by mb128_read_sectors. | |
lda [_di] | |
sta <_al | |
ldy #$01 | |
lda [_di],Y | |
sta <_ah | |
; Substract first byte. | |
sec | |
lda <_cl | |
sbc <_al | |
sta <_cl | |
bcs .l0 | |
dec <_ch | |
.l0: | |
; Substract second byte. | |
sec | |
lda <_cl | |
sbc <_ah | |
sta <_cl | |
bcs .l1 | |
dec <_ch | |
.l1: | |
; Now compare with the first 2 bytes. | |
cmp <_al | |
bne .error | |
cmp <_ah | |
bne .error | |
; Check header string. | |
clc | |
lda <_di | |
adc #$04 | |
sta <_di | |
bcc .l2 | |
inc <_di+1 | |
.l2: | |
lda #low(mb128_string) | |
sta <_si | |
lda #high(mb128_string) | |
sta <_si+1 | |
cly | |
.l3: | |
lda [_si], Y | |
cmp [_di], Y | |
bne .error | |
iny | |
cpy #$0a | |
bne .l3 | |
cla | |
ply | |
plx | |
rts | |
.error: | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Compute header CRC (untested). | |
; in : _si Address of the first sector buffer. | |
; out : _cx CRC | |
;;--------------------------------------------------------------------- | |
mb128_compute_header_CRC: | |
phx | |
phy | |
ldx #$02 | |
ldy #$02 | |
stz <_cx | |
stz <_cx+1 | |
.update256: | |
.update: | |
lda [_si], Y | |
clc | |
adc <_cx | |
sta <_cx | |
bcc .inc0 | |
inc <_cx+1 | |
.inc0: | |
iny | |
bne .update | |
inc <_si+1 | |
dex | |
bne .update256 | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Find the entry matching the specified entry name (untested). | |
; in : _si Address of the first sector buffer | |
; _ax Entry name | |
; out : A $ff if the entry was not found or $00 upon success. | |
; _bl Id of the last visited entry. | |
; _si Address of the entry in sector buffer (if found) | |
; or the address of the first empty entry (if not found) | |
; use : _cx | |
;;--------------------------------------------------------------------- | |
mb128_find_entry: | |
phx | |
phy | |
clx | |
.loop: | |
inx | |
cpx #MB128_ENTRY_COUNT | |
beq .not_found | |
clc | |
lda <_si | |
adc #MB128_ENTRY_SIZE | |
sta <_si | |
bcc .l0 | |
inc <_si+1 | |
.l0: | |
lda [_si] | |
beq .not_found | |
.check: | |
clc | |
; Jump to entry name | |
lda <_si | |
adc #$08 | |
sta <_cx | |
lda <_si+1 | |
adc #$00 | |
sta <_cx+1 | |
cly | |
.check_loop: | |
lda [_ax],Y | |
cmp [_cx],Y | |
; Check next entry if the name does not match | |
bne .loop | |
iny | |
cpy #$08 | |
bne .check_loop | |
; We found the entry | |
.found: | |
cla | |
stx <_bl | |
ply | |
plx | |
rts | |
.not_found: | |
stx <_bl | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Update/Create a new entry (unstested). | |
; in : _si Address of the first sector buffer. | |
; _di Address of the data buffer. | |
; _ax Entry name. | |
; _bh Number of sectors to allocate. | |
; out : A $ff if an error occured. $00 upon success. | |
; _bl Sector id. | |
; use : ? | |
;;--------------------------------------------------------------------- | |
mb128_write_entry: | |
phx | |
phy | |
; Save sector address. | |
lda <_si | |
sta <_bp | |
lda <_si+1 | |
sta <_bp+1 | |
ldy #$01 | |
jsr mb128_find_entry | |
cmp #$00 | |
bne .new_entry | |
.existing_entry: | |
; Check if entry size matches requested size. | |
lda [_si], Y | |
cmp <_bh | |
beq .ok | |
.size_mismatch: | |
; [todo] error code | |
lda #$ff | |
ply | |
plx | |
rts | |
.ok: | |
lda [_si] | |
sta <_bl | |
bra .copy_data | |
.new_entry: | |
lda <_bl | |
cmp #MB128_ENTRY_COUNT | |
beq .no_entry_left | |
tax | |
; Compute sector id | |
ldy #$02 | |
lda [_bp], Y | |
sta <_bl | |
; Set entry sector id | |
sta [_si] | |
; Update total sector count | |
clc | |
adc <_bh | |
sta [_bp], Y | |
bcc .inc0 | |
iny | |
lda [_bp], Y | |
inc A | |
sta [_bp], Y | |
.inc0: | |
; Set entry sector count | |
ldy #$01 | |
lda <_bh | |
sta [_si], Y | |
iny | |
; Set dummy bytes | |
cla | |
sta [_si], Y | |
iny | |
lda #$02 | |
sta [_si], Y | |
iny | |
; Set entry name | |
lda <_si | |
clc | |
adc #$08 | |
sta <_si | |
lda <_si+1 | |
adc #$00 | |
sta <_si+1 | |
cly | |
.name: | |
lda [_ax], Y | |
sta [_si], Y | |
iny | |
cpy #$08 | |
bne .name | |
.copy_data: | |
lda <_di | |
sta <_si | |
lda <_di+1 | |
sta <_si+1 | |
jsr mb128_write_sectors | |
cmp #$00 | |
bne .write_error | |
; Update entry CRC | |
lda mb128_entry_off.lo, X | |
clc | |
adc <_bp | |
sta <_si | |
lda mb128_entry_off.hi, X | |
adc <_bp+1 | |
sta <_si | |
ldy #4 | |
lda <_cx | |
sta [_si], Y | |
iny | |
lda <_cx+1 | |
sta [_si], Y | |
; Update header CRC | |
lda <_bp | |
sta <_si | |
lda <_bp+1 | |
sta <_si+1 | |
jsr mb128_compute_header_CRC | |
lda <_cx | |
sta [_bp] | |
ldy #$01 | |
sta [_bp], Y | |
ply | |
plx | |
rts | |
.error: | |
.write_error: | |
; [todo] error code | |
.no_entry_left: | |
; [todo] error code | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Read entry (untested). | |
; in : <_al Entry id. | |
; <_si Address of the first sector buffer. | |
; <_di Output buffer address. | |
; out : A $ff if an error occured. $00 upon success. | |
; (todo) empty entry, (todo) invalid CRC. | |
; <_bl Sector id. | |
; <_bh Sector count. | |
;;--------------------------------------------------------------------- | |
mb128_read_entry: | |
phx | |
phy | |
; Compute address | |
ldx <_al | |
lda <_si | |
pha | |
adc mb128_entry_off.lo, X | |
sta <_dx | |
lda <_si+1 | |
pha | |
adc mb128_entry_off.hi, X | |
sta <_dx+1 | |
; Sector id. | |
lda [_dx] | |
beq .err | |
sta <_bl | |
; Sector count. | |
ldy #$01 | |
lda [_dx], Y | |
sta <_bh | |
; Read data and compute CRC. | |
jsr mb128_read_sectors | |
cmp #$00 | |
beq .err | |
; Check CRC against the one stored in entry info. | |
ldy #$04 | |
lda [_dx], Y | |
cmp <_cx | |
bne .err | |
iny | |
lda [_dx], Y | |
cmp <_cx+1 | |
bne .err | |
.ok: | |
cla | |
ply | |
plx | |
rts | |
.err: | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Format header. | |
; in : _si Address of the first sector buffer | |
; out : | |
; use : _di | |
;;--------------------------------------------------------------------- | |
mb128_format: | |
phx | |
phy | |
; Compute destination | |
lda <_si | |
clc | |
adc #$01 | |
sta <_di | |
lda <_si+1 | |
adc #$00 | |
sta <_di+1 | |
cla | |
sta [_si] | |
; Clear | |
tsx | |
sxy | |
lda #$60 ; rts | |
pha | |
lda #high($400) ; length (hi) | |
pha | |
lda #low($400) ; length (lo) | |
pha | |
lda <_di+1 ; destination (hi) | |
pha | |
lda <_di ; desination (lo) | |
pha | |
lda <_si+1 ; source (hi) | |
pha | |
lda <_si ; source (lo) | |
pha | |
lda #$73 ; tii | |
pha | |
; Compute jump address | |
tsx | |
txa | |
clc | |
adc #low($2100) ; Stack address | |
sta <_di | |
lda #high($2100) | |
adc #$00 | |
sta <_di+1 | |
jsr .format | |
; Restore stack pointer | |
sxy | |
txs | |
cly | |
.copy: | |
lda mb128_default_header, Y | |
sta [_si], Y | |
iny | |
cpy #MB128_ENTRY_SIZE | |
bne .copy | |
ply | |
plx | |
rts | |
.format: | |
jmp [_di] | |
; [todo] mb128_delete_entry |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Memory Base 128 | |
Author : MooZ | |
Version : 0.1 | |
Description: | |
------------ | |
The NEC Memory Base 128 (mb128 for short) is an external PC-Engine RAM backup | |
unit that plugs between the joypad port and the joypad itself. It can stores up | |
to 128 KB, which is 64 times more than the standard Backup Units (Tennokoe 2 or | |
Duo internal BRAM). Just like the Tennokoe 2, the mb128 needs to be powered by | |
external batteries (4 AA) in order to retain data. It is built around the | |
MSM6389C from OKI Semiconductor, a 1048576 x 1bit solid-state data register. | |
This means that data is transfered 1 bit at a time. | |
Note that Koei released a similar device (if not a clone), the Save Kun. | |
Basic operations: | |
----------------- | |
As the mb128 is plugged into the joyport, communication is done through the | |
joypad control port ($1000). | |
Without further ado, here is how to send a bit to the mb128. At this point | |
the bit may not be stored. There is a bit of protocol to respect before getting | |
anything written on the MSM6389C (more on this later). | |
In the following routine the A register holds the bit (0 or 1) to be sent. If | |
you have ever looked at a joypad routine, you will recognize the classic | |
pha/pla/nop delay used in many games and in MagicKit. | |
mb128_send_bit: | |
and #$01 | |
sta joyport | |
pha | |
pla | |
nop | |
ora #$02 | |
sta joyport | |
pha | |
pla | |
pha | |
pla | |
pha | |
pla | |
and #$01 | |
sta joyport | |
pha | |
pla | |
nop | |
rts | |
If you are not familiar with assembly languages, the mb128_send_bit routine can | |
be translated into the following timing diagram where . means a cycle of delay, | |
b is the bit to send (either 0 or 1). There are 2 lines. The one labeled w is | |
for the data written to joyport. The r line is for the data read from the | |
joyport. Only 4 bits can be read or write through the joyport. | |
w: {000b}{.........}{001b}{.....................}{000b}{.........} | |
r: | |
A byte is transfered by sending each bit starting from bit 0 to bit 7. This can | |
easily be done by repeatedly shifting the value to the right and sending the | |
carry flag to the mb128. This can be translated in the following C-like | |
pseudocode. | |
mb128_send_byte: | |
for(i=0; i<8; i++) | |
{ | |
mb128_send_bit( a & 1 ); | |
a = a >> 1; | |
} | |
Reading a bit is performed in a similar fashion. The assembly routine looks like | |
this : | |
mb128_read_bit: | |
stz joyport | |
pha | |
pla | |
nop | |
lda #$02 | |
sta joyport | |
pha | |
pla | |
nop | |
lda joyport | |
stz joyport | |
pha | |
pla | |
and #$01 | |
rts | |
The associated timing diagram : | |
w: {0000}{.........}{0010}{.........} {0000}{.........} | |
r: {abcd} | |
Reading a byte is done just like its counterpart. A byte is read by performing | |
8 consecutive bit read and pushing the bit using left shift. | |
mb128_read_byte: | |
a = 0; | |
for(i=0; i<8; i++) | |
{ | |
a = (a << 1) | mb128_read_bit(); | |
} | |
Detection & boot: | |
----------------- | |
The following sequence let you detect the presence of a mb128. | |
mb128_detect: | |
mb128_send_byte( 0xA8 ); | |
mb128_send_bit( 0 ); | |
res = joyport << 4; | |
mb128_send_bit(1); | |
res = res | (joyport & 0x0F); | |
A mb128 is plugged to the joyport if res is equal to 4. Some games make 3 | |
attempts before calling it quits | |
At startup just after detection, a special sequence is performed. It can be | |
viewed as a boot/reset sequence. | |
mb128_boot: | |
mb128_send_bit( 1 ); | |
mb128_send_bit( 0 ); | |
mb128_send_bit( 0 ); | |
mb128_send_byte( 0x00 ); | |
mb128_send_byte( 0x01 ); | |
mb128_send_byte( 0x00 ); | |
mb128_send_bit( 0 ); | |
mb128_send_bit( 0 ); | |
mb128_send_bit( 0 ); | |
mb128_send_bit( 0 ); | |
mb128_read_bit(); | |
mb128_send_bit( 0 ); | |
mb128_send_bit( 0 ); | |
mb128_send_bit( 0 ); | |
Once mb128_detect and mb128_boot are performed, you can safely read or write | |
data to the mb128 storage. | |
Sector read/write: | |
------------------ | |
All games studied store or retrieve data by blocs of 512 bytes. A bloc is called | |
a sector. | |
The first thing to do is to tell the mb128 which sector is being processed. As | |
the mb128 can stored up to 128KB, there are 256 (0x100) available sectors. The | |
sequence sent is similar to the one sent for a boot sequence (mb128_boot). | |
mb128_sector_addr: | |
mb128_send_bit( 1 ); | |
mb128_send_bit( 0 ); | |
mb128_send_bit( 0 ); | |
mb128_send_byte( sector_id ); | |
mb128_send_byte( 0x00 ); | |
mb128_send_byte( 0x10 ); | |
mb128_send_bit( 0 ); | |
mb128_send_bit( 0 ); | |
mb128_send_bit( 0 ); | |
mb128_send_bit( 0 ); | |
Once the sector address is set byte can be read or written using mb128_read or | |
mb128_write. A standard multi-sector read routine looks like this : | |
mb128_read_sectors: | |
for( b=0; b<sector_count; b++ ) | |
{ | |
if( mb128_detect() == success ) | |
{ | |
mb128_sector_addr( sector + b ); | |
for( i=0; i<512; i++ ) | |
{ | |
out[i] = mb128_read_byte(); | |
} | |
} | |
} | |
And similarly for a multi-sector write we have : | |
mb128_write_sectors: | |
for( b=0; b<sector_count; b++ ) | |
{ | |
if( mb128_detect() == success ) | |
{ | |
mb128_sector_addr( sector + b ); | |
for( i=0; i<512; i++ ) | |
{ | |
mb128_send_byte( in[i] ); | |
} | |
} | |
} | |
Header format: | |
-------------- | |
The first 2 sectors (1024 bytes) of the mb128 holds what can be describe as an | |
entry list. Each entry is 16 bytes long. This means that the those sector can | |
hold 64 entries. | |
The first entry contains the header. It is organized as follow : | |
byte 0 : CRC (lsb) | |
1 : CRC (msb) | |
2 : Used sector count (lsb) | |
3 : Used sector count (msb) | |
4 : Header string (メモリベース128\x0000) | |
. | |
. | |
. | |
f | |
The Header CRC is the sum of the bytes 0x02 to 0x3ff. Some games (Shin Megami | |
Tensei for example) keep bytes 2 and 3 at zero. | |
Next comes the savegame entries. | |
byte 0 : sector number | |
1 : sector count | |
2 : unknown (0x00) | |
3 : unknown (0x02) | |
4 : CRC (lsb) | |
5 : CRC (msb) | |
6 : unknown (0x00) | |
7 : unknown (0x00) | |
8 : entry name | |
. | |
. | |
. | |
f | |
The first 2 bytes tell where the data is stored (sector number) and how many | |
sectors are used. The meaning of bytes 2, 3, 4 and 5 is unknown. None of the | |
game studied are using them, but they all store the same values. That is 0x00 | |
and 0x02 for bytes 2 and 3, and 0x00 for both bytes 6 and 7. | |
The CRC is sum of the stored bytes. This can be translated in the following | |
pseudo-C code. | |
u8 out[ sector_count * 512 ]; | |
u16 crc = 0; | |
mb128_read( sector, sector_count ); | |
for( i=0; i<sector_count; i++ ) | |
{ | |
for( j=0; j<512; j++ ) | |
{ | |
crc += out[ (i*512) + j ]; | |
} | |
} | |
The entry name is a 8 bytes string. It is supposed to be unique allowing games | |
to retrieve their data. | |
Here are some examples : | |
* "ユウシャM128" for Tadaima Yusha Boshuuchuu. | |
* "MT0 ", "MT1 ", ... for Shin Megami Tensei. | |
Shin Megami Tensei allows the player to have up to 10 savestates. Where Tadaima | |
Yusha Boshuuchuu only allows 1 savestate. | |
The format of the data stored is not standardized. This means that they are | |
game dependent. For example, it seems that Tadaima Yusha Boshuuchuu is using | |
the mb128 as an extra BRAM. On the other hand, Shin Megami Tensei has its own | |
internal format. | |
Todo: | |
----- | |
Test : | |
* mb128_clear_sectors | |
* mb128_compute_header_CRC | |
* mb128_find_entry | |
* mb128_read_entry | |
* mb128_clear_sectors | |
Contact: | |
-------- | |
mooz at blockos dot org | |
License: | |
-------- | |
This document is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0). | |
Example code: | |
------------- | |
The following source code is just meant as an example. | |
Use it at your own risk. | |
;;--{file:begin mb128.asm}--------------------------------------------- | |
;;--------------------------------------------------------------------- | |
; Constants. | |
;;--------------------------------------------------------------------- | |
; Number of bytes per sector. | |
MB128_SECTOR_SIZE=$200 | |
; Maximum number of sectors. | |
MB128_SECTOR_COUNT=$100 | |
; Number of bytes per entry. | |
MB128_ENTRY_SIZE=$10 | |
; Maximum number of entries. | |
MB128_ENTRY_COUNT=$40 | |
; Detection retry count at boot (0 = 256) | |
MB128_BOOT_RETRY_COUNT=8 | |
;;--------------------------------------------------------------------- | |
; Default header. | |
;;--------------------------------------------------------------------- | |
mb128_default_header: | |
.db $30,$06,$00,$00 | |
;;--------------------------------------------------------------------- | |
; Header string. | |
;;--------------------------------------------------------------------- | |
mb128_string: | |
.db $d2,$d3,$d8,$cd,$de,$b0,$bd,$31,$32,$38,$00,$00 | |
;;--------------------------------------------------------------------- | |
; Entry offsets. | |
;;--------------------------------------------------------------------- | |
mb128_entry_off.lo: | |
.dwl $0000, $0010, $0020, $0030, $0040, $0050, $0060, $0070 | |
.dwl $0080, $0090, $00A0, $00B0, $00C0, $00D0, $00E0, $00F0 | |
.dwl $0100, $0110, $0120, $0130, $0140, $0150, $0160, $0170 | |
.dwl $0180, $0190, $01A0, $01B0, $01C0, $01D0, $01E0, $01F0 | |
.dwl $0200, $0210, $0220, $0230, $0240, $0250, $0260, $0270 | |
.dwl $0280, $0290, $02A0, $02B0, $02C0, $02D0, $02E0, $02F0 | |
.dwl $0300, $0310, $0320, $0330, $0340, $0350, $0360, $0370 | |
.dwl $0380, $0390, $03A0, $03B0, $03C0, $03D0, $03E0, $03F0 | |
mb128_entry_off.hi: | |
.dwh $0000, $0010, $0020, $0030, $0040, $0050, $0060, $0070 | |
.dwh $0080, $0090, $00A0, $00B0, $00C0, $00D0, $00E0, $00F0 | |
.dwh $0100, $0110, $0120, $0130, $0140, $0150, $0160, $0170 | |
.dwh $0180, $0190, $01A0, $01B0, $01C0, $01D0, $01E0, $01F0 | |
.dwh $0200, $0210, $0220, $0230, $0240, $0250, $0260, $0270 | |
.dwh $0280, $0290, $02A0, $02B0, $02C0, $02D0, $02E0, $02F0 | |
.dwh $0300, $0310, $0320, $0330, $0340, $0350, $0360, $0370 | |
.dwh $0380, $0390, $03A0, $03B0, $03C0, $03D0, $03E0, $03F0 | |
;;--------------------------------------------------------------------- | |
; Error values. | |
;;--------------------------------------------------------------------- | |
; [todo] | |
;;--------------------------------------------------------------------- | |
; Write a single bit to memory base 128. | |
; in : A Bit to send. | |
; out : | |
;;--------------------------------------------------------------------- | |
mb128_send_bit: | |
and #$01 | |
sta joyport | |
pha | |
pla | |
nop | |
ora #$02 | |
sta joyport | |
pha | |
pla | |
pha | |
pla | |
pha | |
pla | |
and #$01 | |
sta joyport | |
pha | |
pla | |
nop | |
rts | |
;;--------------------------------------------------------------------- | |
; Write a single byte to memory base 128. | |
; in : A Byte to send. | |
; out : | |
; use : _al | |
;;--------------------------------------------------------------------- | |
mb128_send_byte: | |
phx | |
ldx #$08 | |
sta <_al | |
.loop: | |
lsr <_al | |
cla | |
rol A | |
sta joyport | |
pha | |
pla | |
nop | |
ora #$02 | |
sta joyport | |
pha | |
pla | |
pha | |
pla | |
pha | |
pla | |
and #$01 | |
sta joyport | |
pha | |
pla | |
nop | |
dex | |
bne .loop | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Read a single bit from memory base 128. | |
; in : | |
; out : A bit read. | |
;;--------------------------------------------------------------------- | |
mb128_read_bit: | |
stz joyport | |
pha | |
pla | |
nop | |
lda #$02 | |
sta joyport | |
pha | |
pla | |
nop | |
lda joyport | |
stz joyport | |
pha | |
pla | |
and #$01 | |
rts | |
;;--------------------------------------------------------------------- | |
; Read a single byte from memory base 128. | |
; in : | |
; out : A Byte read. | |
; use : _al | |
;;--------------------------------------------------------------------- | |
mb128_read_byte: | |
phx | |
stz <_al | |
ldx #$08 | |
.loop: | |
stz joyport | |
pha | |
pla | |
nop | |
lda #$02 | |
sta joyport | |
pha | |
pla | |
nop | |
lda joyport | |
lsr A | |
ror <_al | |
sta joyport | |
pha | |
pla | |
nop | |
dex | |
bne .loop | |
plx | |
lda <_al | |
rts | |
;;--------------------------------------------------------------------- | |
; Detect if a memory base 128 is present. | |
; in : | |
; out : A $ff if detection failed, $00 if a memory base 128 was | |
; succesfully detected. | |
; use : _dl, _al | |
;;--------------------------------------------------------------------- | |
mb128_detect: | |
phx | |
ldx #$03 | |
.loop: | |
lda #$a8 | |
jsr mb128_send_byte | |
cla | |
jsr mb128_send_bit | |
lda joyport | |
asl A | |
asl A | |
asl A | |
asl A | |
sta <_dl | |
lda #$01 | |
jsr mb128_send_bit | |
lda joyport | |
and #$0f | |
ora <_dl | |
cmp #$04 | |
beq .found | |
dex | |
bne .loop | |
.not_found: | |
lda #$ff | |
plx | |
rts | |
.found: | |
cla | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Detect and reset Memory Base 128 states. | |
; in : | |
; out : A $ff if detection failed, $00 if a memory base 128 was | |
; succesfully detected. | |
; use : _al, _dl | |
;;--------------------------------------------------------------------- | |
mb128_boot: | |
phx | |
phy | |
ldx #MB128_BOOT_RETRY_COUNT | |
.retry: | |
jsr mb128_detect | |
cmp #$00 | |
beq .init | |
dex | |
bne .retry | |
.fail: | |
lda #$ff | |
ply | |
plx | |
rts | |
.init: | |
lda #$01 | |
jsr mb128_send_bit | |
cla | |
jsr mb128_send_bit | |
jsr mb128_send_bit | |
jsr mb128_send_byte | |
lda #$01 | |
jsr mb128_send_byte | |
cla | |
jsr mb128_send_byte | |
cla | |
jsr mb128_send_bit | |
jsr mb128_send_bit | |
jsr mb128_send_bit | |
jsr mb128_send_bit | |
jsr mb128_read_bit | |
cla | |
jsr mb128_send_bit | |
jsr mb128_send_bit | |
jsr mb128_send_bit | |
.success: | |
cla | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Set internal memory base 128 address to the specified sector. | |
; in : _bl Sector number | |
; out : | |
; use : _al | |
;;--------------------------------------------------------------------- | |
mb128_sector_addr: | |
lda #$01 | |
jsr mb128_send_bit | |
cla | |
jsr mb128_send_bit | |
jsr mb128_send_bit | |
lda <_bl | |
jsr mb128_send_byte | |
cla | |
jsr mb128_send_byte | |
lda #$10 | |
jsr mb128_send_byte | |
cla | |
jsr mb128_send_bit | |
jsr mb128_send_bit | |
jsr mb128_send_bit | |
jsr mb128_send_bit | |
rts | |
;;--------------------------------------------------------------------- | |
; Write data from source buffer to the specified memory base 128 | |
; sectors. | |
; in : _bl Sector number | |
; _bh Sector count | |
; _si Source pointer | |
; out : _cx CRC | |
; A $ff if an error occured, $00 upon success. | |
; use : _al, _dl | |
;;--------------------------------------------------------------------- | |
mb128_write_sectors: | |
phx | |
phy | |
stz <_cx | |
stz <_cx+1 | |
.write_next_sector: | |
; Write a sector | |
jsr mb128_detect | |
cmp #$00 | |
bne .error | |
jsr mb128_sector_addr | |
ldx #$02 | |
.write_next_256: | |
cly | |
.write_next_byte: | |
lda [_si], Y | |
jsr mb128_send_byte | |
iny | |
bne .write_next_byte | |
inc <_si+1 | |
dex | |
bne .write_next_256 | |
; Rewind source buffer | |
dec <_si+1 | |
dec <_si+1 | |
; Compare read data with source | |
jsr mb128_detect | |
cmp #$00 | |
bne .error | |
jsr mb128_sector_addr | |
ldx #$02 | |
.check_next_256: | |
cly | |
.check_next_byte: | |
jsr mb128_read_byte | |
cmp [_si], Y | |
bne .error | |
clc | |
adc <_cl | |
sta <_cl | |
bcc .inc0 | |
inc <_ch | |
.inc0: | |
iny | |
bne .check_next_byte | |
inc <_si+1 | |
dex | |
bne .check_next_256 | |
inc <_bl | |
dec <_bh | |
bne .write_next_sector | |
.ok: | |
cla | |
ply | |
plx | |
rts | |
.error: | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Fill specified memory base 128 sectors with 0 (untested). | |
; in : _bl Sector id | |
; _bh Sector count | |
; out : A $ff if an error occured, $00 upon success. | |
; use : _al, _dl | |
;;--------------------------------------------------------------------- | |
mb128_clear_sectors: | |
phx | |
phy | |
.clear_next_sector: | |
; Write a sector | |
jsr mb128_detect | |
cmp #$00 | |
bne .error | |
jsr mb128_sector_addr | |
ldx #$02 | |
.clear_next_256: | |
cly | |
.clear_next_byte: | |
cla | |
jsr mb128_send_byte | |
iny | |
bne .clear_next_byte | |
inc <_si+1 | |
dex | |
bne .clear_next_256 | |
; Check data | |
jsr mb128_detect | |
cmp #$00 | |
bne .error | |
jsr mb128_sector_addr | |
ldx #$02 | |
.check_next_256: | |
cly | |
.check_next_byte: | |
jsr mb128_read_byte | |
cmp #$00 | |
bne .error | |
iny | |
bne .check_next_byte | |
inc <_si+1 | |
dex | |
bne .check_next_256 | |
inc <_bl | |
dec <_bh | |
bne .clear_next_sector | |
.ok: | |
cla | |
ply | |
plx | |
rts | |
.error: | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Read data from specified memory base 128 sectors and store it to the | |
; destination buffer. | |
; in : _bl Sector id | |
; _bh Sector count | |
; _di Destination pointer | |
; out : _cx CRC | |
; A $ff if an error occured, $00 upon success. | |
; use : _al, _dl | |
;;--------------------------------------------------------------------- | |
mb128_read_sectors: | |
phx | |
phy | |
stz <_cl | |
stz <_ch | |
.read_next_sector: | |
jsr mb128_detect | |
cmp #$00 | |
bne .error | |
jsr mb128_sector_addr | |
ldx #$02 | |
.read_next_256: | |
cly | |
.read_next_byte: | |
jsr mb128_read_byte | |
sta [_di], Y | |
; CRC is just the sum of all bytes | |
clc | |
adc <_cl | |
sta <_cl | |
bcc .l0 | |
inc <_ch | |
.l0: | |
iny | |
bne .read_next_byte | |
inc <_di+1 | |
dex | |
bne .read_next_256 | |
inc <_bl | |
dec <_bh | |
bne .read_next_sector | |
.ok: | |
cla | |
ply | |
plx | |
rts | |
.error: | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Read memory base 128 first sector header. | |
; in : _di Pointer to destination buffer containing the first sector. | |
; out : A $ff if an error occured. $00 upon success. | |
; use : _ax, _bx, _cx, _dl, _si | |
;;--------------------------------------------------------------------- | |
mb128_read_header: | |
phx | |
phy | |
stz <_bl | |
lda #$02 | |
sta <_bh | |
jsr mb128_read_sectors | |
cmp #$00 | |
bne .error | |
; The first 2 bytes of the sectors is the sum of all sector bytes | |
; minus the first 2 bytes. | |
; In order to perform a valid check we must first substract them | |
; from the CRC computed by mb128_read_sectors. | |
lda [_di] | |
sta <_al | |
ldy #$01 | |
lda [_di],Y | |
sta <_ah | |
; Substract first byte. | |
sec | |
lda <_cl | |
sbc <_al | |
sta <_cl | |
bcs .l0 | |
dec <_ch | |
.l0: | |
; Substract second byte. | |
sec | |
lda <_cl | |
sbc <_ah | |
sta <_cl | |
bcs .l1 | |
dec <_ch | |
.l1: | |
; Now compare with the first 2 bytes. | |
cmp <_al | |
bne .error | |
cmp <_ah | |
bne .error | |
; Check header string. | |
clc | |
lda <_di | |
adc #$04 | |
sta <_di | |
bcc .l2 | |
inc <_di+1 | |
.l2: | |
lda #low(mb128_string) | |
sta <_si | |
lda #high(mb128_string) | |
sta <_si+1 | |
cly | |
.l3: | |
lda [_si], Y | |
cmp [_di], Y | |
bne .error | |
iny | |
cpy #$0a | |
bne .l3 | |
cla | |
ply | |
plx | |
rts | |
.error: | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Compute header CRC (untested). | |
; in : _si Address of the first sector buffer. | |
; out : _cx CRC | |
;;--------------------------------------------------------------------- | |
mb128_compute_header_CRC: | |
phx | |
phy | |
ldx #$02 | |
ldy #$02 | |
stz <_cx | |
stz <_cx+1 | |
.update256: | |
.update: | |
lda [_si], Y | |
clc | |
adc <_cx | |
sta <_cx | |
bcc .inc0 | |
inc <_cx+1 | |
.inc0: | |
iny | |
bne .update | |
inc <_si+1 | |
dex | |
bne .update256 | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Find the entry matching the specified entry name (untested). | |
; in : _si Address of the first sector buffer | |
; _ax Entry name | |
; out : A $ff if the entry was not found or $00 upon success. | |
; _bl Id of the last visited entry. | |
; _si Address of the entry in sector buffer (if found) | |
; or the address of the first empty entry (if not found) | |
; use : _cx | |
;;--------------------------------------------------------------------- | |
mb128_find_entry: | |
phx | |
phy | |
clx | |
.loop: | |
inx | |
cpx #MB128_ENTRY_COUNT | |
beq .not_found | |
clc | |
lda <_si | |
adc #MB128_ENTRY_SIZE | |
sta <_si | |
bcc .l0 | |
inc <_si+1 | |
.l0: | |
lda [_si] | |
beq .not_found | |
.check: | |
clc | |
; Jump to entry name | |
lda <_si | |
adc #$08 | |
sta <_cx | |
lda <_si+1 | |
adc #$00 | |
sta <_cx+1 | |
cly | |
.check_loop: | |
lda [_ax],Y | |
cmp [_cx],Y | |
; Check next entry if the name does not match | |
bne .loop | |
iny | |
cpy #$08 | |
bne .check_loop | |
; We found the entry | |
.found: | |
cla | |
stx <_bl | |
ply | |
plx | |
rts | |
.not_found: | |
stx <_bl | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Update/Create a new entry (unstested). | |
; in : _si Address of the first sector buffer. | |
; _di Address of the data buffer. | |
; _ax Entry name. | |
; _bh Number of sectors to allocate. | |
; out : A $ff if an error occured. $00 upon success. | |
; _bl Sector id. | |
; use : ? | |
;;--------------------------------------------------------------------- | |
mb128_write_entry: | |
phx | |
phy | |
; Save sector address. | |
lda <_si | |
sta <_bp | |
lda <_si+1 | |
sta <_bp+1 | |
ldy #$01 | |
jsr mb128_find_entry | |
cmp #$00 | |
bne .new_entry | |
.existing_entry: | |
; Check if entry size matches requested size. | |
lda [_si], Y | |
cmp <_bh | |
beq .ok | |
.size_mismatch: | |
; [todo] error code | |
lda #$ff | |
ply | |
plx | |
rts | |
.ok: | |
lda [_si] | |
sta <_bl | |
bra .copy_data | |
.new_entry: | |
lda <_bl | |
cmp #MB128_ENTRY_COUNT | |
beq .no_entry_left | |
tax | |
; Compute sector id | |
ldy #$02 | |
lda [_bp], Y | |
sta <_bl | |
; Set entry sector id | |
sta [_si] | |
; Update total sector count | |
clc | |
adc <_bh | |
sta [_bp], Y | |
bcc .inc0 | |
iny | |
lda [_bp], Y | |
inc A | |
sta [_bp], Y | |
.inc0: | |
; Set entry sector count | |
ldy #$01 | |
lda <_bh | |
sta [_si], Y | |
iny | |
; Set dummy bytes | |
cla | |
sta [_si], Y | |
iny | |
lda #$02 | |
sta [_si], Y | |
iny | |
; Set entry name | |
lda <_si | |
clc | |
adc #$08 | |
sta <_si | |
lda <_si+1 | |
adc #$00 | |
sta <_si+1 | |
cly | |
.name: | |
lda [_ax], Y | |
sta [_si], Y | |
iny | |
cpy #$08 | |
bne .name | |
.copy_data: | |
lda <_di | |
sta <_si | |
lda <_di+1 | |
sta <_si+1 | |
jsr mb128_write_sectors | |
cmp #$00 | |
bne .write_error | |
; Update entry CRC | |
lda mb128_entry_off.lo, X | |
clc | |
adc <_bp | |
sta <_si | |
lda mb128_entry_off.hi, X | |
adc <_bp+1 | |
sta <_si | |
ldy #4 | |
lda <_cx | |
sta [_si], Y | |
iny | |
lda <_cx+1 | |
sta [_si], Y | |
; Update header CRC | |
lda <_bp | |
sta <_si | |
lda <_bp+1 | |
sta <_si+1 | |
jsr mb128_compute_header_CRC | |
lda <_cx | |
sta [_bp] | |
ldy #$01 | |
sta [_bp], Y | |
ply | |
plx | |
rts | |
.error: | |
.write_error: | |
; [todo] error code | |
.no_entry_left: | |
; [todo] error code | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Read entry (untested). | |
; in : <_al Entry id. | |
; <_si Address of the first sector buffer. | |
; <_di Output buffer address. | |
; out : A $ff if an error occured. $00 upon success. | |
; (todo) empty entry, (todo) invalid CRC. | |
; <_bl Sector id. | |
; <_bh Sector count. | |
;;--------------------------------------------------------------------- | |
mb128_read_entry: | |
phx | |
phy | |
; Compute address | |
ldx <_al | |
lda <_si | |
pha | |
adc mb128_entry_off.lo, X | |
sta <_dx | |
lda <_si+1 | |
pha | |
adc mb128_entry_off.hi, X | |
sta <_dx+1 | |
; Sector id. | |
lda [_dx] | |
beq .err | |
sta <_bl | |
; Sector count. | |
ldy #$01 | |
lda [_dx], Y | |
sta <_bh | |
; Read data and compute CRC. | |
jsr mb128_read_sectors | |
cmp #$00 | |
beq .err | |
; Check CRC against the one stored in entry info. | |
ldy #$04 | |
lda [_dx], Y | |
cmp <_cx | |
bne .err | |
iny | |
lda [_dx], Y | |
cmp <_cx+1 | |
bne .err | |
.ok: | |
cla | |
ply | |
plx | |
rts | |
.err: | |
lda #$ff | |
ply | |
plx | |
rts | |
;;--------------------------------------------------------------------- | |
; Format header. | |
; in : _si Address of the first sector buffer | |
; out : | |
; use : _di | |
;;--------------------------------------------------------------------- | |
mb128_format: | |
phx | |
phy | |
; Compute destination | |
lda <_si | |
clc | |
adc #$01 | |
sta <_di | |
lda <_si+1 | |
adc #$00 | |
sta <_di+1 | |
cla | |
sta [_si] | |
; Clear | |
tsx | |
sxy | |
lda #$60 ; rts | |
pha | |
lda #high($400) ; length (hi) | |
pha | |
lda #low($400) ; length (lo) | |
pha | |
lda <_di+1 ; destination (hi) | |
pha | |
lda <_di ; desination (lo) | |
pha | |
lda <_si+1 ; source (hi) | |
pha | |
lda <_si ; source (lo) | |
pha | |
lda #$73 ; tii | |
pha | |
; Compute jump address | |
tsx | |
txa | |
clc | |
adc #low($2100) ; Stack address | |
sta <_di | |
lda #high($2100) | |
adc #$00 | |
sta <_di+1 | |
jsr .format | |
; Restore stack pointer | |
sxy | |
txs | |
cly | |
.copy: | |
lda mb128_default_header, Y | |
sta [_si], Y | |
iny | |
cpy #MB128_ENTRY_SIZE | |
bne .copy | |
ply | |
plx | |
rts | |
.format: | |
jmp [_di] | |
;;--{file:end mb128.asm}----------------------------------------------- | |
;;--{file:begin system.inc}-------------------------------------------- | |
;;--------------------------------------------------------------------- | |
; Zero page variables | |
;;--------------------------------------------------------------------- | |
; Copy of vdc registers | |
vdc_crl = $20f3 ; VDC control register (lo) | |
vdc_crh = $20f4 ; (hi) | |
irq_m = $20f5 ; interrupt control mask | |
vdc_sr = $20f6 ; VDC status register | |
vdc_reg = $20f7 ; VDC register index | |
; Standard parameter-passing areas (in zero-page) for subroutines. | |
_bp = $20EC ; Base pointer | |
_si = $20EE ; Source address | |
_di = $20F0 ; Destination address | |
_ax = $20F8 ; Temporary variables | |
_al = $20F8 | |
_ah = $20F9 | |
_bx = $20FA | |
_bl = $20FA | |
_bh = $20FB | |
_cx = $20FC | |
_cl = $20FC | |
_ch = $20FD | |
_dx = $20FE | |
_dl = $20FE | |
_dh = $20FF | |
;;--------------------------------------------------------------------- | |
; System ports | |
;;--------------------------------------------------------------------- | |
;;--------------------------------------------------------------------- | |
; VDC (Video Display Controller) | |
;;--------------------------------------------------------------------- | |
videoport .equ $0000 | |
; VDC register port | |
video_reg .equ videoport | |
video_reg_l .equ video_reg | |
video_reg_h .equ video_reg+1 | |
; VDC data | |
video_data .equ videoport+2 | |
video_data_l .equ video_data | |
video_data_h .equ video_data+1 | |
;;--------------------------------------------------------------------- | |
; VCE (Video Color Encoder) | |
;;--------------------------------------------------------------------- | |
colorport .equ $0400 | |
color_ctrl .equ colorport | |
color_reg .equ colorport+2 | |
color_reg_l .equ color_reg | |
color_reg_h .equ color_reg+1 | |
color_data .equ colorport+4 | |
color_data_l .equ color_data | |
color_data_h .equ color_data+1 | |
;;--------------------------------------------------------------------- | |
; PSG (Programmable Sound Generator) | |
;;--------------------------------------------------------------------- | |
psgport .equ $0800 | |
psg_ch .equ psgport | |
psg_mainvol .equ psgport+1 | |
psg_freq.lo .equ psgport+2 | |
psg_freq.hi .equ psgport+3 | |
psg_ctrl .equ psgport+4 | |
psg_pan .equ psgport+5 | |
psg_wavebuf .equ psgport+6 | |
psg_noise .equ psgport+7 | |
psg_lfoctrl .equ psgport+9 | |
psg_lfofreq .equ psgport+8 | |
;;--------------------------------------------------------------------- | |
; TIMER | |
;;--------------------------------------------------------------------- | |
timerport .equ $0C00 | |
timer_cnt .equ timerport | |
timer_ctrl .equ timerport+1 | |
;;--------------------------------------------------------------------- | |
; I/O port | |
;;--------------------------------------------------------------------- | |
joyport .equ $1000 | |
;;--------------------------------------------------------------------- | |
; IRQ ports | |
;;--------------------------------------------------------------------- | |
irqport .equ $1400 | |
irq_disable .equ irqport+2 | |
irq_status .equ irqport+3 | |
;;--------------------------------------------------------------------- | |
; This block defines names for macro | |
; argument types (\?x). | |
;;--------------------------------------------------------------------- | |
ARG_NONE .equ 0 | |
ARG_REG .equ 1 | |
ARG_IMMED .equ 2 | |
ARG_ABS .equ 3 | |
ARG_ABSOLUTE .equ 3 | |
ARG_INDIRECT .equ 4 | |
ARG_STRING .equ 5 | |
ARG_LABEL .equ 6 | |
;;--{file:end system.inc}--------------------------------------------- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.bank 0 | |
.org $fff6 | |
interrupt_vectors: | |
.dw irq_2 | |
.dw irq_1 | |
.dw irq_timer | |
.dw irq_nmi | |
.dw irq_reset | |
.org $e000 | |
.code | |
vdcInitTable: | |
; reg low hi | |
.db $07, $00, $00 ; Background x-scroll register | |
.db $08, $00, $00 ; Background y-scroll register | |
.db $09, $10, $00 ; Background map size | |
.db $0A, $02, $02 ; Horizontal period register | |
.db $0B, $1F, $04 ; Horizontal display register | |
.db $0C, $02, $17 ; Vertical sync register | |
.db $0D, $DF, $00 ; Vertical display register | |
.db $0E, $0C, $00 ; Vertical display position end register | |
irq_reset: | |
sei ; Disable interrupts | |
csh ; Switch CPU to hi-speed mode (7.16MHz) | |
cld ; Clear decimal flag (numbers are not BCD encoded) | |
ldx #$ff ; Set stack pointer | |
txs | |
lda #$ff ; Map the I/O bank to the first page | |
tam #$0 | |
lda #$f8 ; Map the RAM bank to the second page | |
tam #$1 | |
stz $2000 ; Clear RAM | |
tii $2000, $2001, $1fff | |
lda #%11111101 ; Disable timer and IRQ1 interrupts. | |
sta irq_disable | |
stz irq_status | |
lda #$01 ; Disable timer | |
sta timer_ctrl | |
cly ; Setup VDC display registers | |
.l0: | |
lda vdcInitTable, Y | |
sta video_reg | |
iny | |
lda vdcInitTable, Y | |
sta video_data_l | |
iny | |
lda vdcInitTable, Y | |
sta video_data_h | |
iny | |
cpy #24 | |
bne .l0 | |
cly ; Disable sound | |
.l1: | |
sty psg_ch | |
stz psg_ctrl | |
iny | |
cpy #6 | |
bne .l1 | |
cli ; Enable iterrupts | |
jmp main | |
irq_2: | |
irq_1: | |
irq_timer: | |
irq_nmi: | |
rti |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;--------------------------------------------------------------------- | |
; Zero page variables | |
;;--------------------------------------------------------------------- | |
; Copy of vdc registers | |
vdc_crl = $20f3 ; VDC control register (lo) | |
vdc_crh = $20f4 ; (hi) | |
irq_m = $20f5 ; interrupt control mask | |
vdc_sr = $20f6 ; VDC status register | |
vdc_reg = $20f7 ; VDC register index | |
; Standard parameter-passing areas (in zero-page) for subroutines. | |
_bp = $20EC ; Base pointer | |
_si = $20EE ; Source address | |
_di = $20F0 ; Destination address | |
_ax = $20F8 ; Temporary variables | |
_al = $20F8 | |
_ah = $20F9 | |
_bx = $20FA | |
_bl = $20FA | |
_bh = $20FB | |
_cx = $20FC | |
_cl = $20FC | |
_ch = $20FD | |
_dx = $20FE | |
_dl = $20FE | |
_dh = $20FF | |
;;--------------------------------------------------------------------- | |
; System ports | |
;;--------------------------------------------------------------------- | |
;;--------------------------------------------------------------------- | |
; VDC (Video Display Controller) | |
;;--------------------------------------------------------------------- | |
videoport .equ $0000 | |
; VDC register port | |
video_reg .equ videoport | |
video_reg_l .equ video_reg | |
video_reg_h .equ video_reg+1 | |
; VDC data | |
video_data .equ videoport+2 | |
video_data_l .equ video_data | |
video_data_h .equ video_data+1 | |
;;--------------------------------------------------------------------- | |
; VCE (Video Color Encoder) | |
;;--------------------------------------------------------------------- | |
colorport .equ $0400 | |
color_ctrl .equ colorport | |
color_reg .equ colorport+2 | |
color_reg_l .equ color_reg | |
color_reg_h .equ color_reg+1 | |
color_data .equ colorport+4 | |
color_data_l .equ color_data | |
color_data_h .equ color_data+1 | |
;;--------------------------------------------------------------------- | |
; PSG (Programmable Sound Generator) | |
;;--------------------------------------------------------------------- | |
psgport .equ $0800 | |
psg_ch .equ psgport | |
psg_mainvol .equ psgport+1 | |
psg_freq.lo .equ psgport+2 | |
psg_freq.hi .equ psgport+3 | |
psg_ctrl .equ psgport+4 | |
psg_pan .equ psgport+5 | |
psg_wavebuf .equ psgport+6 | |
psg_noise .equ psgport+7 | |
psg_lfoctrl .equ psgport+9 | |
psg_lfofreq .equ psgport+8 | |
;;--------------------------------------------------------------------- | |
; TIMER | |
;;--------------------------------------------------------------------- | |
timerport .equ $0C00 | |
timer_cnt .equ timerport | |
timer_ctrl .equ timerport+1 | |
;;--------------------------------------------------------------------- | |
; I/O port | |
;;--------------------------------------------------------------------- | |
joyport .equ $1000 | |
;;--------------------------------------------------------------------- | |
; IRQ ports | |
;;--------------------------------------------------------------------- | |
irqport .equ $1400 | |
irq_disable .equ irqport+2 | |
irq_status .equ irqport+3 | |
;;--------------------------------------------------------------------- | |
; This block defines names for macro | |
; argument types (\?x). | |
;;--------------------------------------------------------------------- | |
ARG_NONE .equ 0 | |
ARG_REG .equ 1 | |
ARG_IMMED .equ 2 | |
ARG_ABS .equ 3 | |
ARG_ABSOLUTE .equ 3 | |
ARG_INDIRECT .equ 4 | |
ARG_STRING .equ 5 | |
ARG_LABEL .equ 6 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment