Skip to content

Instantly share code, notes, and snippets.

@logicplace
Last active September 7, 2022 13:15
Show Gist options
  • Save logicplace/4a4143942894ef1bbba7f4fda3c5906d to your computer and use it in GitHub Desktop.
Save logicplace/4a4143942894ef1bbba7f4fda3c5906d to your computer and use it in GitHub Desktop.
#include <pm.h>
#include <stdint.h>
// These are extra return values
int8_t reg_L, reg_B;
union {
struct {
uint8_t u150a; // $150a
uint8_t length; // $150b
uint8_t start_p; // $150c
uint8_t *start; // $150d
uint16_t suffix; // $150f
uint8_t u1511; // $1511
} send;
struct { // sizeof 28
uint8_t u150a; // $150a
uint8_t u150b; // $150b
uint8_t u150c; // $150c
uint8_t u150d[2]; // $150b
uint8_t u150f; // $150f bitmask of some sort
uint8_t u1510; // $1510
uint8_t u1511; // $1511
uint8_t *u1512; // $1512
union {
uint8_t *u1514; // $1514
} ptr;
union {
uint8_t u1514; // $1514
uint8_t u1515; // $1515
} end;
uint8_t data[4]; // $1516
uint8_t u151a; // $151a
uint8_t u151b; // $151b
uint8_t u151c; // $151c
uint8_t *u151d; // $151d
uint8_t u151f[7]; // $151f
} recv;
} working;
// more referenced, $1527~$152f, but seem to be separate structures
uint8_t* /*_at(0x152c) */ buffer;
extern uint8_t flags;
void send(uint8_t byte);
void wait_for(int cycles);
// @0031dd - Tetris (EU)
void ir_out(
uint8_t H,
uint8_t* /*_far*/ start,
uint8_t length,
uint8_t A
) {
// Put in global struct
working.send.u150a = A;
working.send.length = length;
working.send.start_p = (uint16_t)start >> 16;
working.send.start = (uint16_t)start & 0xffff;
working.send.u1511 = H;
int i, res = 0;
if (length) {
uint8_t *s = start;
res = i = length;
for (; i; --i) {
res += *s;
++s;
}
}
res += A;
working.send.suffix = res;
if (H != 1) {
IRQ_ACT4 = IRQ4_IR_RECV;
i = 0x300;
do {
_nop();
if (!--i) break;
} while (!(IRQ_ACT4 & IRQ4_IR_RECV));
wait_for(812); // 2 + (6 + 2 + 2) * 0x51
}
if (length == 0) {
start = &res;
}
for (i = 18;;) {
ir_pulse();
if (--i) break;
wait_for(68);
}
wait_for(70);
wait_for(120); // @0034df
wait_for(61);
send(0);
wait_for(51);
// XI set from start param for these..
send(A);
wait_for(51);
send(length);
wait_for(40); // + 11 for preparing argument
send(A ^ length);
wait_for(20);
int max_bytes = 3;
// waits omitted after this point (sorry)
for (i = length; i;) {
while (i) {
send(*start);
if (!--max_bytes) break;
++start;
--i;
}
if (max_bytes) {
// Bytes have been completely sent
switch (max_bytes) {
case 1:
// remaining length was 2
start -= 2; // back to initial byte
send(*start ^ *(start + 1));
break;
case 2:
// remaining length was 1
--start; // back to initial byte
send(*start);
break;
case 3:
// remaining length was 0
break;
}
break;
} else {
// remaining length was 3 or higher
// More bytes to send, send parity check
start -= 2; // back to initial byte
send(*start ^ *(start + 1) ^ *(start + 2));
max_bytes = 4; // +1 for the parity byte
}
}
send(res >> 8);
send(res & 0xff);
send((res >> 8) ^ (res & 0xff));
}
// @003404 - Tetris (EU)
void send(uint8_t byte) {
int i, num_set = 0;
wait_for(6);
ir_pulse();
wait_for(67);
for (i = 8;;) {
if (byte & (1 << (i-1))) {
ir_pulse();
++num_set;
} else {
wait_for(120); // length of an ir_pulse (@0034df)
}
if (!--i) break;
wait_for(61);
}
// $00345f
if (num_set & 1) {
num_set = 0;
wait_for(61);
ir_pulse();
} else {
num_set = 0;
wait_for(61);
wait_for(120); // length of an ir_pulse (@0034df)
}
return;
}
// @0034b1 - Tetris (EU)
// pulse for 16 cycles (120 total cycles taken (incl call/ret))
void ir_pulse(void) {
_push("IY", "B");
_ld("IY", 0x2061); // IO_DATA
_ld("B", 4); // pulse for 16 cycles (each DJR is 4 cycles)
_int(0x4c); // ir_pulse in BIOS
_pop("B", "IY");
wait_for(63);
return;
}
#define fun_003c7c(H) \
(working.recv.u150a = max( \
working.recv.u150a - (14 * (18 - H)), 0), \
1)
uint8_t fun_00351a(uint8_t A, uint8_t *IX, uint16_t IY) {
int i, j, L, res, *cur, *out;
// BR = 0x20 and zeroes EP, XP, and YP
if (A >= 0x80) {
return 5;
}
// $00352a
*cur = working.recv.data;
for (i = sizeof(working.recv); i; --i) {
*cur = 0;
++cur;
}
// $00353e
working.recv.u1510 = A;
working.recv.u1512 = IX;
working.recv.u151d = IX;
IRQ_ACT4 = IRQ4_IR_RECV; // clear IR IRQ
// $003551
i = 18;
L = 0;
IY = IY >= 24 ? IY - 23 : 1;
do {
_nop();
if(!--IY) break;
} while(!(IRQ_ACT4 & IRQ4_IR_RECV));
working.recv.u150a = IY;
wait_for(5);
for (j = 13;;) {
wait_for(6);
if(!--j) break;
}
wait_for(3);
for (reg_B = 9;;) {
res = read_bit(L);
if (!--i) break;
if (res != 1) {
return fun_003c7c(i);
}
wait_for(109);
}
// $0035c3
if (res != 0) {
return fun_003c7c(i);
}
wait_for(109);
// $0035ff
for (cur = working.recv.data;;) {
res = read_bit(L);
if (res == -1) {
// $003c96
return 2;
} else if(reg_B == 1) {
// $0036bd
if (res != (L & 1)) {
L = 0;
i ^= *cur;
if (cur == 0x1519) {
res = working.recv.u151a;
wait_for(15);
break;
}
++cur;
wait_for(79);
} else {
// $003700
if (working.recv.u151a != 0) {
// $003c99
return 3;
}
working.recv.u151a = 1;
working.recv.ptr.u1514 = cur;
L = 0;
if (cur == 0x1519) {
res = 1;
break;
}
++cur;
wait_for(59);
}
} else if(reg_B == 10) {
// $003684
if (res != 1) {
// $003ca5
return -2;
}
wait_for(97);
} else if (res == 0) {
// $003650
*cur = (*cur << 1);
wait_for(90); // +4 for OR and INC
} else {
// $00361b
*cur = (*cur << 1) | 1;
++L;
wait_for(86);
}
}
// $00374c
cur = working.recv.data;
if (res == 0) {
working.recv.u150a = *cur++;
working.recv.u150b = *cur++;
res = working.recv.u150c = *cur;
wait_for(8);
} else {
*working.recv.ptr.u1514 = i;
working.recv.u150a = *cur++;
working.recv.u150b = *cur++;
res = working.recv.u150c = *cur;
}
// $00377c
if (res >= 3) {
if (res - 1 >= working.recv.u1510) {
// $003c9f
return 5;
}
working.recv.u150f = 0x03;
wait_for(3);
// $003790
for (;;) {
for (;;) {
res = read_bit(L);
if (res == -1) {
// $003c96
return 2;
} else if(reg_B == 1) {
// $0038ea
if (res != (L & 1)) {
L = 0;
i ^= *cur;
if (cur == 0x1519) {
// $00396d
res = working.recv.u151a;
wait_for(15);
break;
}
++cur;
wait_for(79);
} else {
// $00392d
if (working.recv.u151a != 0) {
// $003c99
return 3;
}
working.recv.u151a = 1;
working.recv.ptr.u1514 = cur;
L = 0;
if (cur == 0x1519) {
res = 1;
break;
}
++cur;
wait_for(59);
}
} else if(reg_B == 10) {
// $003812
if (res != 1) {
// $003ca5
return -2;
}
if (1 & working.recv.u150f) {
// $003855
i = 0; L = 0;
working.recv.u151a = 0;
working.recv.data[3] = 0;
working.recv.data[2] = 0;
working.recv.data[1] = 0;
working.recv.data[0] = 0;
working.recv.u150f &= 0xfe;
wait_for(52);
} else if (2 & working.recv.u150f) {
// $00388d
working.recv.u151b = (uint8_t)(working.recv.u150c / 3);
working.recv.u151c = working.recv.u150c % 3;
working.recv.u150f &= 0xfd;
wait_for(31);
} else if (4 & working.recv.u150f) {
// $0038bf
working.recv.u151d += 3;
working.recv.u150f &= 0xfb;
wait_for(35);
}
wait_for(70);
} else if (res == 0) {
// $0037de
*cur = (*cur << 1);
wait_for(90); // +4 for OR and INC
} else {
// $0037a9
*cur = (*cur << 1) | 1;
++L;
wait_for(86);
}
}
// $003979
if (res == 0) {
out = working.recv.u151d;
cur = working.recv.data;
*out++ = *cur++;
*out++ = *cur++;
*out = *cur;
wait_for(8);
} else {
*working.recv.ptr.u1514 = i;
out = working.recv.u151d;
cur = working.recv.data;
*out++ = *cur++;
*out++ = *cur++;
*out = *cur;
}
if ((res = working.recv.u151b - 1)) {
working.recv.u151b = res;
L = -1;
_nop();
} else {
// $0039cb
wait_for(6);
break;
}
}
} else {
// $0039bc
working.recv.u151c = res;
working.recv.u150f = 0xff;
wait_for(4);
}
// $0039d1
res = read_bit(L);
if (res == -1) {
// $003c96
return 2;
}
switch (working.recv.u151c) {
case 0:
// $0039e9
res = 0x1d;
wait_for(4);
break;
case 1:
// $0039ed
res = 0x31;
break;
default:
// $0039e5
res = 0x3b;
break;
}
L = res;
working.recv.u151c = res + 1;
cur = working.recv.u151f;
*cur = 1;
i = 7;
wait_for(83);
// $003a2b
for (;;) {
res = read_bit(L);
if (res == -1) {
// $003c96
return 2;
}
// $003a33
if (res != 1) {
*cur = *cur << 1;
wait_for(1);
} else {
// $003a3e
*cur = (*cur << 1) | 1;
}
// $003a44
if(!--i) {
++cur;
i = 8;
} else {
wait_for(6);
}
if (!--L) break;
wait_for(85);
}
// $003a81
for (; i; --i) {
// $003a86
*cur = *cur << 1;
}
// $003a8d
if (working.recv.u1510 < working.recv.u150c) {
// $003c9f
return 5;
}
if (working.recv.u150f != 0xff) {
working.recv.u151d += 3;
}
L = working.recv.u151c;
working.recv.end.u1514 = (uint8_t)(L / 10);
working.recv.data[0] = 0;
working.recv.end.u1515 = 0;
working.recv.data[1] = 10;
out = cur = working.recv.u151f;
i = 8;
_ld("A", 0); // waste cycles
res = *cur;
// $003ad6
for (;;) {
switch (--working.recv.data[1]) {
case 9:
// $003af9
res <<= 1;
break;
case 0:
// $003aff
uint8_t tmp = working.recv.end.u1515, tL, tC;
*out = tmp;
tL = 0;
for (j = 8;;) {
tC = tmp & 0x80; // carry flag
tmp <<= 1;
if (tC) ++tL;
if (!--j) break;
}
tL &= 1;
res <<= 1;
_rl(tmp); // 0xce91
tmp &= 1;
if (tL == tmp) {
// $003b26
working.recv.data[0] <<= 1;
} else {
// $003b32
working.recv.data[0] = (working.recv.data[0] << 1) | 1;
}
// $003b3e
working.recv.data[1] = 10;
++out;
break;
default:
res <<= 1;
_rl(working.recv.end.u1515); // 0xce91
}
// $003b4b
if (!--L) break;
if (!--i) {
i = 8;
++cur;
res = *cur; // from $003ad5
}
}
// $003b56
switch(working.recv.end.u1514) {
case 3:
// 3c06
break;
case 5:
// 3bb7
break;
default:
working.recv.data[0] <<= 2;
if (fun_003ca8(0x1518, 0x151f, 3) == 1) {
// $003c99
return 3;
}
break;
}
// $003c33
}
// @003cd5 - Tetris (EU)
int8_t read_bit(int8_t L) {
int i;
wait_for(1);
IRQ_ACT4 = IRQ4_IR_RECV; // clear IR IRQ
if (--reg_B) {
// $003cff
wait_for(51);
return IRQ_ACT4 & IRQ4_IR_RECV ? 1 : 0;
} else {
reg_B = 10;
for (i = 10;;) {
if (!--i) return -1;
if (IRQ_ACT4 & IRQ4_IR_RECV) break;
}
if (L == -1) {
working.recv.u150f = 0x05;
} else {
wait_for(9);
}
wait_for(6);
return 1;
}
}
// @003df6 - Tetris (EU)
void /*_interrupt(...)*/ ir_recv() {
flags &= 0xcf; // reset interrupt priority
IRQ_ACT4 = IRQ4_IR_RECV; // clear IR IRQ
switch (fun_003ffa()) {
case 0:
*(uint8_t*)0x1528 = reg_B + 0x80;
IRQ_ENA4 &= ~IRQ4_IR_RECV; // disable IR IRQ
break;
case 3:
*(uint8_t*)0x1528 = 2;
IRQ_ENA4 &= ~IRQ4_IR_RECV; // disable IR IRQ
break;
default:
break;
}
IRQ_ACT4 = IRQ4_IR_RECV; // clear IR IRQ
}
// @003ffa - Tetris (EU)
uint8_t fun_003ffa() {
int ret, res;
// These are pushed to the stack in pairs via HL
uint8_t IRQ_ENA1_bak = IRQ_ENA1;
uint8_t IRQ_ENA2_bak = IRQ_ENA2;
uint8_t IRQ_ENA4_bak = IRQ_ENA4;
uint8_t PRC_MODE_bak = PRC_MODE;
IRQ_ENA1 = 0;
IRQ_ENA2 &= IRQ2_CART_EJECT | IRQ2_CART;
IRQ_ENA3 &= ~(IRQ4_IR_RECV | IRQ4_SHOCK);
if (is_prc_busy()) {
// $004087
ret = 1;
} else {
PRC_MODE = 0;
*buffer = 0;
res = fun_00351a(0x0050, *(uint16_t*)0x00152a, *(uint8_t*)0x152f);
*buffer = res;
switch (res) {
case -1:
// $00408b
ret = 2;
break;
case 1:
// $004093
ret = 4;
break;
case 0:
// $004046
*(uint8_t*)0x152d = reg_L;
if (reg_B == -1 || *(uint8_t*)0x1527 == reg_B) {
// $004056
ir_out(1, buffer, 1, 0);
ret = 0;
} else {
// $00408f
ret = 3;
}
break;
default:
ret = 1;
break;
}
}
// $004067
IRQ_ACT1 = 0xff;
IRQ_ACT2 = 0xff;
IRQ_ACT3 = 0xff;
IRQ_ACT4 = 0xff;
IRQ_ENA4 = IRQ_ENA4_bak;
PRC_MODE = PRC_MODE_bak;
IRQ_ENA1 = IRQ_ENA1_bak;
IRQ_ENA2 = IRQ_ENA2_bak;
reg_B = *(uint8_t*)0x152d;
return ret;
}
// @0040be - Tetris (EU)
uint8_t is_prc_busy() {
// TODO
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment