Just posting an update on the Dune mouse patch. Here's my current code to read the mouse. Of note, it assembles (using asmx) to 58 bytes smaller than the original code (400 bytes vs 458) and handles overflow and sign properly. Tested on my Model 2 Genesis + Model 2 CD + 32X with both a Japanese Sega Mouse and a US Mega Mouse, it's smooth as silk.
Note: I could pull the main loops for reading the header and packet out as a subroutine to make the code even smaller, but I wasn't trying to make the code as small as possible, just smaller than the original code despite the extra processing on the packet.
Code: Select all
; ---------------------------------------------------------------------------
org $2460
mouse_read:
; halt z80
move.w #$100,($A11100).l
move.w #$100,($A11200).l
wait_z80:
btst #0,($A11100).l
bne.s wait_z80
movea.l #$A10005,a0
move.b #$60,6(a0) ; set port direction for TLH
nop
nop
move.b #$60,(a0) ; set port for TLH
nop
nop
wait_hs1:
btst #4,(a0)
beq.s wait_hs1
; get header
move.w #254,d1 ; # retries
moveq #1,d2 ; two bytes (four nibbles) in header
hdr_loop:
move.b (a0),d0
move.b #$20,(a0) ; next phase
lsl.b #4,d0
lsl.l #4,d0 ; save nibble
wait_hs2:
btst #4,(a0)
bne.s hdr_phase2
dbra d1,wait_hs2
bra.w mouse_err
hdr_phase2:
move.b (a0),d0
move.b #0,(a0) ; next phase
lsl.b #4,d0
lsl.l #4,d0 ; save nibble
wait_hs3:
btst #4,(a0)
beq.s hdr_chklp
dbra d1,wait_hs3
bra.w mouse_err
hdr_chklp:
dbra d2,hdr_loop
lsr.l #8,d0
; check header
cmpi.w #$0BFF,d0 ; check for mouse
bne.w mouse_err
; get mouse packet
moveq #2,d2 ; three bytes (six nibbles) in packet
pkt_loop:
move.b (a0),d0
move.b #$20,(a0) ; next phase
lsl.b #4,d0
lsl.l #4,d0 ; save nibble
wait_hs4:
btst #4,(a0)
bne.s pkt_phase2
dbra d1,wait_hs4
bra.w mouse_err
pkt_phase2:
move.b (a0),d0
move.b #0,(a0) ; next phase
lsl.b #4,d0
lsl.l #4,d0 ; save nibble
wait_hs5:
btst #4,(a0)
beq.s pkt_chklp
dbra d1,wait_hs5
bra.w mouse_err
pkt_chklp:
dbra d2,pkt_loop
lsr.l #8,d0
move.b #$60,(a0) ; TLH done
move.w #0,($A11100).l ; release z80
; process mouse packet
; d0 = YO XO YS XS S M R L X7 X6 X5 X4 X3 X2 X1 X0 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
moveq #0,d2
move.b d0,d2
btst #23,d0 ; YO
beq.s chkys ; no overflow
move.w #$0100,d2
btst #21,d0 ; YS
beq.s calcy ; positive
neg.w d2
bra.s calcy
chkys:
btst #21,d0 ; YS
beq.s calcy ; positive
or.w #$FF00,d2 ; sign-extend Y
tst.b d2
bne.s calcy
seq d2 ; handle -0 case
calcy:
asr.w #1,d2
move.w ($FFFFBF14).w,d1 ; screen Y coord
sub.w d2,d1
cmpi.w #$D0,d1
ble.s *+6
move.w #$D0,d1
cmpi.w #$10,d1
bge.s *+6
move.w #$10,d1
move.w d1,($FFFFBF14).w ; update Y coord
lsr.w #8,d0
btst #22,d0 ; XO
beq.s chkxs ; no overflow
move.w #$0100,d0
btst #20,d0 ; XS
beq.s calcx ; positive
neg.w d0
bra.s calcx
chkxs:
btst #20,d0 ; XS
beq.s calcx ; positive
or.w #$FF00,d0 ; sign-extend X
tst.b d0
bne.s calcx
seq d0 ; handle -0 case
calcx:
asr.w #1,d0
move.w ($FFFFBF12).w,d1 ; screen X coord
add.w d0,d1
cmpi.w #$130,d1
ble.s *+6
move.w #$130,d1
cmpi.w #$10,d1
bge.s *+6
move.w #$10,d1
move.w d1,($FFFFBF12).w ; update X coord
clr.b d1
btst #16,d0 ; LMB
beq.s *+6
bset #6,d1
btst #17,d0 ; RMB
beq.s *+6
bset #4,d1
move.b d1,d0
move.b ($FFFFE41C).w,d1 ; save current button state
move.b d0,($FFFFE41C).w ; set current button state
eor.b d0,d1
move.b d1,($FFFFE41D).w ; change in button state
rts
; ---------------------------------------------------------------------------
mouse_err:
move.b #$60,(a0)
nop
nop
wait_err:
btst #4,(a0)
beq.s wait_err
moveq #-1,d0
move.w #0,($A11100).l ; release z80
rts
EDIT: Pulling the three-line handshake out as a subroutine reduces the size to 346 bytes. No noticeable impact on the game (I didn't expect any as the cost of the bsr/rts is minimal compared to the overall length of mouse handling).
Code: Select all
; ---------------------------------------------------------------------------
org $2460
mouse_read:
; halt z80
move.w #$100,($A11100).l
move.w #$100,($A11200).l
wait_z80:
btst #0,($A11100).l
bne.s wait_z80
movea.l #$A10005,a0
move.b #$60,6(a0) ; set port direction for TLH
nop
nop
move.b #$60,(a0) ; set port for TLH
nop
nop
wait_hs0:
btst #4,(a0)
beq.s wait_hs0
move.w #254,d1 ; # retries
; get header
moveq #1,d2 ; two bytes (four nibbles) in header
bsr.w do_tlh
; check header
cmpi.w #$0BFF,d0 ; check for mouse
bne.w mouse_err
; get mouse packet
moveq #2,d2 ; three bytes (six nibbles) in packet
bsr.w do_tlh
move.b #$60,(a0) ; TLH done
move.w #0,($A11100).l ; release z80
; process mouse packet
; d0 = YO XO YS XS S M R L X7 X6 X5 X4 X3 X2 X1 X0 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
btst #23,d0 ; YO
beq.s chkys ; no overflow
move.w #$0100,d2
btst #21,d0 ; YS
beq.s calcy ; positive
neg.w d2
bra.s calcy
chkys:
moveq #0,d2
move.b d0,d2
btst #21,d0 ; YS
beq.s calcy ; positive
or.w #$FF00,d2 ; sign-extend Y
tst.b d2
bne.s calcy
seq d2 ; handle -0 case
calcy:
asr.w #1,d2
move.w ($FFFFBF14).w,d1 ; screen Y coord
sub.w d2,d1
cmpi.w #$D0,d1
ble.s chkymin
move.w #$D0,d1
chkymin:
cmpi.w #$10,d1
bge.s updsy
move.w #$10,d1
updsy:
move.w d1,($FFFFBF14).w ; update Y coord
btst #22,d0 ; XO
beq.s chkxs ; no overflow
move.w #$0100,d0
btst #20,d0 ; XS
beq.s calcx ; positive
neg.w d0
bra.s calcx
chkxs:
lsr.w #8,d0
btst #20,d0 ; XS
beq.s calcx ; positive
or.w #$FF00,d0 ; sign-extend X
tst.b d0
bne.s calcx
seq d0 ; handle -0 case
calcx:
asr.w #1,d0
move.w ($FFFFBF12).w,d1 ; screen X coord
add.w d0,d1
cmpi.w #$130,d1
ble.s chkxmin
move.w #$130,d1
chkxmin:
cmpi.w #$10,d1
bge.s updsx
move.w #$10,d1
updsx:
move.w d1,($FFFFBF12).w ; update X coord
clr.b d1
btst #16,d0 ; LMB
beq.s chkrmb
bset #6,d1
chkrmb:
btst #17,d0 ; RMB
beq.s do_btns
bset #4,d1
do_btns:
move.b d1,d0
move.b ($FFFFE41C).w,d1 ; save current button state
move.b d0,($FFFFE41C).w ; set current button state
eor.b d0,d1
move.b d1,($FFFFE41D).w ; change in button state
rts
do_tlh:
move.b (a0),d0
move.b #$20,(a0) ; next phase
lsl.b #4,d0
lsl.l #4,d0 ; save nibble
wait_hs1:
btst #4,(a0)
bne.s pkt_phase2
dbra d1,wait_hs1
addq.l #4,sp ; pop return address
bra.s mouse_err
pkt_phase2:
move.b (a0),d0
move.b #0,(a0) ; next phase
lsl.b #4,d0
lsl.l #4,d0 ; save nibble
wait_hs2:
btst #4,(a0)
beq.s pkt_chklp
dbra d1,wait_hs2
addq.l #4,sp ; pop return address
bra.s mouse_err
pkt_chklp:
dbra d2,do_tlh
lsr.l #8,d0
rts
; ---------------------------------------------------------------------------
mouse_err:
move.b #$60,(a0) ; TLH done
move.w #0,($A11100).l ; release z80
moveq #-1,d0
rts
EDIT 2: Note that while the code above halts the Z80, strictly speaking, it isn't necessary. There's a bug in the MD where under certain conditions, if you don't halt the Z80 while reading the IO area, the Z80 rom access cycle time becomes shortened until you reset the MD. With the original old, slow roms Sega and other devs used when the system first came out, you did need to halt the Z80. However, devs soon realized that higher speed roms could still meet the faster cycle time, so it didn't matter if the cycle time was shortened. As faster roms became cheaper, more and more games quit halting the Z80 while accessing IO. The code above only halts the Z80 because the original patch did, and I wanted to leave the patch alone as much as possible while fixing mouse handling.