Skip to content

Instantly share code, notes, and snippets.

@BlockoS
Last active November 29, 2015 17:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BlockoS/ad7098771aea4970886b to your computer and use it in GitHub Desktop.
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)
.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
;;---------------------------------------------------------------------
; 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
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}---------------------------------------------
.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
;;---------------------------------------------------------------------
; 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