Skip to content

Instantly share code, notes, and snippets.

@realmonster
Last active June 13, 2021 08:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save realmonster/ae8ef8ad3e89f9b3be72 to your computer and use it in GitHub Desktop.
Save realmonster/ae8ef8ad3e89f9b3be72 to your computer and use it in GitHub Desktop.
Sega Genesis Test ROM with reading of Team Player and 4Way on the fly.
/*
Disclaimer:
Any and all content presented in this sourcecode is
for informational and educational purposes.
You assume any and all responsibility for using this content responsibly.
It's extracted from game Street Racer, formatted, modified and commented by r57shell.
At the request of the original license holders
and developers this sourcecode will be removed.
Keywords for searching changes: was, modified, added.
*/
.bss
.globl teamplayer_port1_data
teamplayer_port1_data:
.skip 4*6 | ds.b 4*6 | (words: type_id, data, type)
.globl teamplayer_port2_data
teamplayer_port2_data:
.skip 4*6 | ds.b 4*6
.globl teamplayer_port1_info
teamplayer_port1_info:
.skip 8 | ds.b 8 | was 4 bytes in original
.globl teamplayer_port2_info
teamplayer_port2_info:
.skip 8 | ds.b 8 | was 8 bytes in original
.globl prev_input
prev_input:
.skip 2*4 | ds.b 2*4 | size = 2x4 bytes (up to 4 players)
.globl new_input
new_input:
.skip 2*4 |
.globl detect_counter
detect_counter:
.skip 2 | ds.b 2 | word
.globl multi_flags
multi_flags:
.skip 2 | ds.b 2 | word (bit 1 = port 1, bit 2 = port 2, bit 8 = 4way)
.globl max_players_count
max_players_count:
.skip 2 | ds.b 2 | word
.text
| -------- Team Player Read Port ---------
teamplayer_read_port: | ROM:000CC6D8
movea.l #0xA10003,%a0
moveq #0,%d7
move.w #0xFFF,%d7 | retry
move.b #0x60,6(%a0,%d6.w)
move.b #0x60,(%a0,%d6.w)
moveq #0,%d0
moveq #0,%d1
move.b (%a0,%d6.w),%d1
andi.b #0xF,%d1
cmpi.b #3,%d1 | 3
bne.w .err | error
move.b #0x20,(%a0,%d6.w)
move.b %d1,(%a1)+
moveq #0,%d2
move.b (%a0,%d6.w),%d2
andi.b #0xF,%d2
cmpi.b #0xF,%d2 | F
bne.w .err | error
move.b %d2,(%a1)+
move.b #0,(%a0,%d6.w) | write 0
.loop1:
btst #4,(%a0,%d6.w) | await ~10
beq.w .ok1
dbf %d7,.loop1 | await ~10
bra.w .err | error
.ok1:
move.b (%a0,%d6.w),%d1
tst.b %d1 | 0
bne.w .err | error
move.b %d1,(%a1)+
move.b #0x20,(%a0,%d6.w) | write 20
moveq #0,%d1
.loop2:
btst #4,(%a0,%d6.w) | await 10
bne.w .ok2
dbf %d7,.loop2 | await 10
bra.w .err | error
.ok2:
move.b (%a0,%d6.w),%d1
andi.b #0xF,%d1
tst.b %d1 | 0
bne.w .err | error
move.b %d1,(%a1)+
move.b #0,(%a0,%d6.w) | write 0
moveq #0,%d1
.loop3:
btst #4,(%a0,%d6.w) | await ~10
beq.w .ok3
dbf %d7,.loop3 | await ~10
bra.w .err | error
.ok3:
move.b (%a0,%d6.w),(%a1)+
move.b #0x20,(%a0,%d6.w) | write 20
nop
nop
.loop4:
btst #4,(%a0,%d6.w) | await 10
bne.w .ok4
dbf %d7,.loop4 | await 10
bra.w .err | error
.ok4:
move.b (%a0,%d6.w),(%a1)+
move.b #0,(%a0,%d6.w) | write 0
nop
nop
.loop5:
btst #4,(%a0,%d6.w) | await ~10
beq.w .ok5
dbf %d7,.loop5 | await ~10
bra.w .err | error
.ok5:
move.b (%a0,%d6.w),(%a1)+
move.b #0x20,(%a0,%d6.w) | write 20
moveq #0,%d0
moveq #1,%d5
.loop6:
btst #4,(%a0,%d6.w) | await 10
bne.w .ok6
dbf %d7,.loop6 | await 10
bra.w .err | error
.ok6:
move.b (%a0,%d6.w),(%a1)+
andi.l #0xF0F0F0F,-(%a1)
bsr.w teamplayer_read_control | a1 - control pointer
| a2 - fill struct
bcs.w .err | error
bsr.w teamplayer_read_control | a1 - control pointer
| a2 - fill struct
bcs.w .err | error
bsr.w teamplayer_read_control | a1 - control pointer
| a2 - fill struct
bcs.w .err | error
bsr.w teamplayer_read_control | a1 - control pointer
| a2 - fill struct
bcs.w .err | error
ori #0,%ccr
move.b #0x60,(%a0,%d6.w)
rts
.err:
move.b #0x60,(%a0,%d6.w) | error
ori #8,%ccr
rts
| ------- Team Player Read Control -------
| a1 - control pointer
| a2 - fill struct
teamplayer_read_control: | ROM:000CC82A
moveq #0,%d0
move.b (%a1)+,%d0
cmpi.b #2,%d0
bhi.s .err1
add.w %d0,%d0
add.w %d0,%d0
jmp .table(%pc,%d0.w)
.table:
bra.w teamplayer_read_pad3
bra.w teamplayer_read_pad6
bra.w teamplayer_read_mouse
.err1:
move.w #0xF,(%a2)+
move.w #0xFFFF,(%a2)+
move.w #0xFFFF,(%a2)+
rts
teamplayer_read_pad6:
bsr.w teamplayer_handshake | return d0
bcs.w .ret
andi.w #0xF,%d0 | rldu
move.w %d0,%d1
bsr.w teamplayer_handshake | return d0
bcs.w .ret
andi.w #0xF,%d0 | sacb
asl.w #4,%d0
or.b %d0,%d1 | sacbrldu
bsr.w teamplayer_handshake | return d0
bcs.w .ret
andi.w #0xF,%d0 | mxyz
asl.w #8,%d0
asl.w #4,%d0
or.w %d0,%d1 | mxyz0000sacbrldu
not.w %d1
move.w #1,(%a2)+
move.w %d1,(%a2)+
move.w #0x606,(%a2)+
.ret:
rts
teamplayer_read_pad3:
bsr.w teamplayer_handshake | return d0
bcs.w .ret1
andi.w #0xF,%d0 | rldu
move.w %d0,%d1
bsr.w teamplayer_handshake | return d0
bcs.w .ret1
asl.w #4,%d0 | sacb
or.b %d0,%d1 | sacbrldu
not.b %d1
move.w #0,(%a2)+
move.w %d1,(%a2)+
move.w #0x303,(%a2)+
rts
.ret1:
move.w #0xF,(%a2)+
move.l #0xF,(%a2)+
ori #1,%ccr
rts
teamplayer_read_mouse: | ROM:000CC8CC
moveq #0,%d2
bsr.w teamplayer_handshake | return d0
bcs.w .err2 | bad
andi.w #0xF,%d0 | ffff = yo xo ys xs
asl.b #4,%d0
move.b %d0,%d2
bsr.w teamplayer_handshake | return d0
bcs.w .err2 | bad
andi.w #0xF,%d0 | smrl = buttons
or.b %d0,%d2 | ffffsmrl
swap %d2
bsr.w teamplayer_handshake | return d0
bcs.w .err2 | bad
andi.w #0xF,%d0 | x high
or.b %d0,%d2
bsr.w teamplayer_handshake | return d0
bcs.w .err2 | bad
asl.w #4,%d2
andi.w #0xF,%d0 | x low
or.b %d0,%d2 | x
asl.w #8,%d2
bsr.w teamplayer_handshake | return d0
bcs.w .err2 | bad
andi.w #0xF,%d0 | y high
asl.w #4,%d0
or.w %d0,%d2
bsr.w teamplayer_handshake | return d0
bcs.w .err2 | bad
andi.w #0xF,%d0 | y low
or.w %d0,%d2 | x y
move.w #2,(%a2)+
swap %d2
move.w %d2,(%a2)+ | buttons
swap %d2
move.w %d2,(%a2)+ | coords
rts
.err2:
move.w #0xF,(%a2)+ | bad
move.l #0xF,(%a2)+
move.l #0xF,(%a2)+
ori #1,%ccr
rts
| return d0
teamplayer_handshake:
bchg #0,%d5
bne.s .p0_z
move.b #0x20,(%a0,%d6.w)
nop
nop
.loop1_z:
btst #4,(%a0,%d6.w)
bne.w .ok1_z
dbf %d7,.loop1_z
bra.s .err_z
.ok1_z:
move.b (%a0,%d6.w),%d0
rts
.p0_z:
move.b #0,(%a0,%d6.w)
nop
nop
.loop2_z:
btst #4,(%a0,%d6.w)
beq.w .ok2_z
dbf %d7,.loop2_z
bra.s .err_z
.ok2_z:
move.b (%a0,%d6.w),%d0
rts
.err_z:
ori #1,%ccr
rts
| -------- Detect Team Player or 4 Way --------
detect_multi: | ROM:000CC9E8
jsr detect_4way
beq.w .ret2
moveq #0,%d6
lea (teamplayer_port1_info).l,%a1 | teamplayer port1 info
lea (teamplayer_port1_data).l,%a2 | teamplayer port1 pads | pad1
bsr.w teamplayer_read_port
bmi.w .skip
addq.w #1,(multi_flags).l | teamplayers
.skip:
moveq #2,%d6
lea (teamplayer_port2_info).l,%a1 | teamplayer port2 info
lea (teamplayer_port2_data).l,%a2 | teamplayer port2 pads | pad1
bsr.w teamplayer_read_port
bmi.w .ret2
addq.w #2,(multi_flags).l | teamplayers
.ret2:
rts
| ------- Plain 3/6 Button Pad Driver -------
read_plain_pad: | ROM:000CCD64
movem.l %d1-%d5/%a0,-(%sp)
moveq #0,%d0
cmpi.w #2,%d1
bhi.w .ret_p
add.w %d1,%d1
movea.l #0xA10003,%a0
move.b #0x40,6(%a0,%d1.w)
nop
nop
move.b #0x40,(%a0,%d1.w)
moveq #0,%d2
nop
nop
nop
move.b (%a0,%d1.w),%d2
cmpi.b #0x70,%d2
beq.w .ret_p
move.b #0,(%a0,%d1.w)
lsl.w #8,%d2
move.b (%a0,%d1.w),%d2
cmpi.b #0x3F,%d2
beq.w .ret_p
move.b #0x40,(%a0,%d1.w)
moveq #0,%d3
nop
nop
nop
move.b (%a0,%d1.w),%d3
move.b #0,(%a0,%d1.w)
lsl.w #8,%d3
move.b (%a0,%d1.w),%d3
move.b #0x40,(%a0,%d1.w)
moveq #0,%d4
nop
nop
nop
move.b (%a0,%d1.w),%d4
move.b #0,(%a0,%d1.w)
lsl.w #8,%d4
move.b (%a0,%d1.w),%d4
move.b #0x40,(%a0,%d1.w)
moveq #0,%d5
nop
nop
nop
move.b (%a0,%d1.w),%d5
move.b #0,(%a0,%d1.w)
lsl.w #8,%d5
move.b (%a0,%d1.w),%d5
move.b #0x40,(%a0,%d1.w)
cmp.w %d2,%d3
bne.w .ret_p
cmp.w %d3,%d4
beq.w .pad3
andi.w #0xF,%d4
bne.w .ret_p
move.b %d2,%d0
lsl.w #4,%d0
lsr.w #8,%d2
move.b %d2,%d0
lsl.b #2,%d0
lsr.w #2,%d0
andi.l #0xFF,%d0
lsl.b #4,%d5
lsl.w #4,%d5
or.w %d5,%d0
ori.l #0x80000000,%d0
bra.w .ret_p
.pad3:
move.b %d2,%d0
lsl.w #4,%d0
lsr.w #8,%d2
move.b %d2,%d0
lsl.b #2,%d0
lsr.w #2,%d0
andi.l #0xFF,%d0
ori.w #0xFF00,%d0 | added line, to avoid pressed MXYZ
.ret_p:
movem.l (%sp)+,%d1-%d5/%a0
rts
| ------- Detect 4 Way -------
detect_4way: | ROM:000CCE60
move.b #0x43,(0xA1000B).l
nop
move.b #0x7C,(0xA10005).l
nop
move.b #0x7F,(0xA1000B).l
nop
move.b #0x7C,(0xA10005).l
nop
move.b (0xA10003).l,%d0
andi.b #3,%d0
cmpi.b #0,%d0
bne.w .not_4way | not 4way
move.w #8,(multi_flags).l | teamplayers
moveq #0,%d0
rts
.not_4way:
move.b #0x40,(0xA1000B).l | not 4way
clr.w (multi_flags).l | teamplayers
moveq #1,%d0
rts
| ----- Main Read Function
.globl read_input
read_input:
movem.l %d0-%a6,-(%sp)
bsr read_input_b
movem.l (%sp)+,%d0-%a6
rts
read_input_b: | ROM:000CD4EA
move.w (detect_counter).l,%d0
andi.w #0x1F,%d0
bne.w .skip_detect
jsr detect_multi
.skip_detect: | ROM:000CD4FC
moveq #0,%d4
move.w (multi_flags).l,%d3 | teamplayers
beq.w process_plain | process 2 plain pads
cmpi.w #8,%d3
beq.w process_4way
move.w %d3,%d0
andi.w #1,%d0
beq.w .teamplayer2
moveq #0,%d6
lea (teamplayer_port1_info).l,%a1 | teamplayer port1 info
lea (teamplayer_port1_data).l,%a2 | teamplayer port1 pads | pad1
jsr teamplayer_read_port
lea (teamplayer_port1_data).l,%a2 | teamplayer port1 pads | pad1
cmpi.b #3,4(%a2)
bne.w .s1
jsr teamplayer_pad_process
.s1:
cmpi.b #6,4(%a2)
bne.w .s2
jsr teamplayer_pad_process
.s2:
addq.l #6,%a2
cmpi.b #3,4(%a2)
bne.w .s3
jsr teamplayer_pad_process
.s3:
cmpi.b #6,4(%a2)
bne.w .s4
jsr teamplayer_pad_process
.s4:
addq.l #6,%a2
cmpi.b #3,4(%a2)
bne.w .s5
jsr teamplayer_pad_process
.s5:
cmpi.b #6,4(%a2)
bne.w .s6
jsr teamplayer_pad_process
.s6:
addq.l #6,%a2
cmpi.b #3,4(%a2)
bne.w .s7
jsr teamplayer_pad_process
.s7:
cmpi.b #6,4(%a2)
bne.w .s8
jsr teamplayer_pad_process
.s8:
cmpi.w #1,%d3
bne.w .teamplayer2
cmpi.w #6,%d4
bls.w plain_pad_if_teamplayer1 | after teamplayer read plain pad from port 2
rts
| ------------- Team Player on Port 2 -------------
.teamplayer2: | ROM:000CD5CA
cmpi.w #1,%d3
bls.w .ret3
cmpi.w #3,%d3
beq.w .skip_pad
jsr process_plain_pad1 | process plain pad 1
.skip_pad:
moveq #2,%d6
lea (teamplayer_port2_info).l,%a1 | teamplayer port2 info
lea (teamplayer_port2_data).l,%a2 | teamplayer port2 pads | pad1
jsr teamplayer_read_port
lea (teamplayer_port2_data).l,%a2 | teamplayer port2 pads | pad1
cmpi.b #3,4(%a2)
bne.w .s9
jsr teamplayer_pad_process
.s9:
cmpi.b #6,4(%a2)
bne.w .s10
jsr teamplayer_pad_process
.s10:
cmpi.w #8,%d4
beq.w .ret3
addq.l #6,%a2
cmpi.b #3,4(%a2)
bne.w .s11
jsr teamplayer_pad_process
.s11:
cmpi.b #6,4(%a2)
bne.w .s12
jsr teamplayer_pad_process
.s12:
cmpi.w #8,%d4
beq.w .ret3
addq.l #6,%a2
cmpi.b #3,4(%a2)
bne.w .s13
jsr teamplayer_pad_process
.s13:
cmpi.b #6,4(%a2)
bne.w .s14
jsr teamplayer_pad_process
.s14:
cmpi.w #8,%d4
beq.w .ret3
addq.l #6,%a2
cmpi.b #3,4(%a2)
bne.w .s15
jsr teamplayer_pad_process
.s15:
cmpi.b #6,4(%a2)
bne.w .ret3
jsr teamplayer_pad_process
.ret3:
rts
teamplayer_pad_process:
move.w (max_players_count).l,%d5
add.w %d5,%d5
cmp.w %d5,%d4
bhi.s .ret4
move.w 2(%a2),%d0 | modified, was move.b 3(%a2),%d0
lea (prev_input).l,%a0 | player 1 prev input
move.w (%a0,%d4.w),%d1 | modified, was move.b (%a0,%d4.w),%d1
move.w %d0,(%a0,%d4.w) | modified, was move.b %d0,(%a0,%d4.w)
jsr process_control
addq.w #2,%d4
.ret4:
rts
| --------- Process 4 Way -----------
process_4way: | ROM:000CD6C0
move.b #0xC,(0xA10005).l
bsr.w .read_and_call
cmpi.w #0,(max_players_count).l
beq.w joy_ret
move.b #0x1C,(0xA10005).l
bsr.w .read_and_call
cmpi.w #1,(max_players_count).l
bls.w joy_ret
move.b #0x2C,(0xA10005).l
bsr.w .read_and_call
cmpi.w #2,(max_players_count).l
bls.w joy_ret
move.b #0x3C,(0xA10005).l
.read_and_call:
moveq #0,%d1
jsr read_plain_pad | read 3/6 pad
cmpi.w #0,%d0
beq.w joy_ret
neg.w %d0
subq.w #1,%d0
lea (prev_input).l,%a0 | player 1 prev input
adda.l %d4,%a0
bra.w call_process_control | process control
| ------------ Process Plain Pads --------------
process_plain:
bsr.w process_plain_pad1 | process 2 pain pads
cmpi.w #0,(max_players_count).l
beq.w joy_ret
moveq #1,%d1 | process plain pad 2
jsr read_plain_pad | read 3/6 pad
cmpi.w #0,%d0
beq.w joy_ret
neg.w %d0
subq.w #1,%d0
lea (prev_input+2).l,%a0 | player 2 prev input
bra.w call_process_control | process control
| Process Plain Pad on Port 2 if Teamplayer on Port 1
plain_pad_if_teamplayer1: | ROM:000CD75A
cmpi.w #2,(max_players_count).l | after teamplayer read plain pad from port 2
bls.w joy_ret
moveq #1,%d1
jsr read_plain_pad | read 3/6 pad
cmpi.w #0,%d0
beq.w joy_ret
neg.w %d0
subq.w #1,%d0
lea (prev_input).l,%a0 | player 1 prev input
adda.l %d4,%a0
bra.w call_process_control | process control
| ----- Process Plain Pad on Port 1 -----
process_plain_pad1:
moveq #0,%d1
jsr read_plain_pad | read 3/6 pad
cmpi.w #0,%d0
beq.w joy_ret
neg.w %d0
subq.w #1,%d0
lea (prev_input).l,%a0 | player 1 prev input
call_process_control:
move.b (%a0),%d1 | process control
move.b %d0,(%a0)
jsr process_control
addq.w #2,%d4
joy_ret:
rts
| -------- Write handling of joypads button HERE --------
| d0 = MXYZ ???? SACB RLDU (1 = pressed) (low word, high word sign = 6 button pad)
| d1 = prev button
| d4 = player id * 2
process_control:
lea new_input,%a0
move.w %d0,(%a0,%d4)
rts
#include "genesis.h"
/*
Example of reading 3/6 button pads directly or from 4Way, Team Player.
Supports up to four controllers.
For informational and educational purposes.
by r57shell.
Feel free to use this file for your own purposes.
*/
static void showPortState();
static u16 posY, posX;
static char *hex = "0123456789ABCDEF";
extern u16 max_players_count;
extern u16 prev_input[4];
extern u16 new_input[4];
extern u16 detect_counter;
extern u16 multi_flags;
extern u8 teamplayer_port1_info[4];
extern u8 teamplayer_port2_info[4];
extern u16 teamplayer_port1_data[4][3];
extern u16 teamplayer_port2_data[4][3];
void JOY_myinit(void)
{
vu8 *pb;
pb = (vu8 *)0xa10009;
*pb = 0x40;
pb = (vu8 *)0xa1000b;
*pb = 0x40;
max_players_count = 4;
detect_counter = 0;
}
void read_input(void);
void vblank_callback(void)
{
read_input();
/* Comment or remove next line to make detection at each frame */
++detect_counter;
}
int main()
{
JOY_setSupport(PORT_1, JOY_SUPPORT_OFF);
JOY_setSupport(PORT_2, JOY_SUPPORT_OFF);
JOY_myinit();
SYS_setVIntPreCallback(vblank_callback);
VDP_setScreenWidth320();
VDP_setHInterrupt(0);
VDP_setHilightShadow(0);
VDP_setPaletteColor(15+16, 0x0222);
VDP_setTextPalette(0);
VDP_setPaletteColor(0, 0x0882);
while(1)
{
VDP_waitVSync();
showPortState();
}
}
static void printChar(char c, u16 state)
{
char temp[2];
temp[0] = c;
temp[1] = 0;
VDP_setTextPalette(state ? 1 : 0);
VDP_drawText(temp, posX, posY);
posX += 2;
}
static void printWord(u16 val, u16 state)
{
char temp[8];
temp[0] = '0';
temp[1] = 'x';
temp[2] = hex[val >> 12];
temp[3] = hex[(val >> 8) & 15];
temp[4] = hex[(val >> 4) & 15];
temp[5] = hex[val & 15];
temp[6] = 0;
VDP_setTextPalette(state ? 1 : 0);
VDP_drawText(temp, posX, posY);
posX += 8;
}
static void printByte(u8 val, u16 state)
{
char temp[4];
temp[0] = hex[(val >> 4) & 15];
temp[1] = hex[val & 15];
temp[2] = 0;
VDP_setTextPalette(state ? 1 : 0);
VDP_drawText(temp, posX, posY);
}
static void showPortState()
{
u16 i, value;
posY = 2;
posX = 2;
VDP_drawText("Multi:", posX, posY);
posX += 6;
printWord(multi_flags, 0);
++posY;
posX = 2;
VDP_drawText("Team1:", posX, posY);
posX += 6;
for(i=0; i<4; i++)
{
printByte(teamplayer_port1_info[4+i], 0);
posX += 3;
}
posY += 1;
posX = 2;
VDP_drawText("Team2:", posX, posY);
posX += 6;
for(i=0; i<4; i++)
{
printByte(teamplayer_port2_info[4+i], 0);
posX += 3;
}
posY = 5;
for(i=0; i<4; i++)
{
posX = 2;
printChar(hex[i+1], 0);
posX -= 1;
printChar(':', 0);
value = new_input[i];
printChar('U', value & BUTTON_UP);
printChar('D', value & BUTTON_DOWN);
printChar('L', value & BUTTON_LEFT);
printChar('R', value & BUTTON_RIGHT);
printChar('A', value & BUTTON_A);
printChar('B', value & BUTTON_B);
printChar('C', value & BUTTON_C);
printChar('S', value & BUTTON_START);
printChar('X', value & 0x4000);
printChar('Y', value & 0x2000);
printChar('Z', value & 0x1000);
printChar('M', value & 0x8000);
posY++;
}
++posY;
for(i=0; i<4; i++)
{
posX = 2;
printChar(hex[i+1], 0);
posX -= 1;
printChar(':', 0);
printWord(teamplayer_port1_data[i][0], 0);
printWord(teamplayer_port1_data[i][1], 0);
printWord(teamplayer_port1_data[i][2], 0);
posY++;
}
++posY;
for(i=0; i<4; i++)
{
posX = 2;
printChar(hex[i+1], 0);
posX -= 1;
printChar(':', 0);
printWord(teamplayer_port2_data[i][0], 0);
printWord(teamplayer_port2_data[i][1], 0);
printWord(teamplayer_port2_data[i][2], 0);
posY++;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment