Files
psxrecomp/runtime/tests/test_sio_digital_command.c
T
2026-10-17 18:01:27 +03:00

158 lines
6.1 KiB
C

/* Plain digital pads shift their ID during every command byte.
* Synthetic bus transactions over the real sio.c; no BIOS/disc/core fixture. */
#include "sio.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
uint32_t sio_snapshot_bytes(void);
void sio_snapshot_write(uint8_t *p);
int sio_snapshot_read(const uint8_t *p, uint32_t len);
uint32_t i_stat = 0;
uint32_t i_mask = 0;
uint32_t g_debug_current_func_addr = 0;
uint32_t g_debug_last_store_pc = 0;
int psx_get_in_exception(void) { return 0; }
uint8_t psx_read_byte(uint32_t addr) { (void)addr; return 0; }
uint32_t psx_read_word(uint32_t addr) { (void)addr; return 0; }
/* sio.c gates card-transfer deferral on `psx_get_cycle_count() < deadline`, so
* a constant clock would defer forever. Advance monotonically. */
uint64_t psx_get_cycle_count(void) {
static uint64_t t;
return t += 64;
}
uint32_t memory_get_sr(void) { return 0; }
void debug_server_poll(void) {}
void debug_server_log_sio_write(uint32_t a, uint32_t v, uint8_t w) {
(void)a; (void)v; (void)w;
}
void starvation_ring_record(uint8_t k, uint8_t tx, uint8_t rx, uint16_t c,
uint16_t st, uint8_t d, uint8_t ss, uint16_t ms,
uint8_t mc, uint16_t sec, uint8_t di, uint32_t f) {
(void)k; (void)tx; (void)rx; (void)c; (void)st; (void)d; (void)ss;
(void)ms; (void)mc; (void)sec; (void)di; (void)f;
}
void card_read_summary_record(uint8_t s, uint8_t c, uint16_t sec, uint8_t e,
uint8_t chk, uint8_t d0, uint8_t d1, uint32_t f) {
(void)s; (void)c; (void)sec; (void)e; (void)chk; (void)d0; (void)d1; (void)f;
}
void card_data_writes_arm(uint8_t v, uint16_t s, uint8_t i, uint8_t sl) {
(void)v; (void)s; (void)i; (void)sl;
}
void event_ring_record_aux(uint16_t k, uint8_t d, uint32_t a) {
(void)k; (void)d; (void)a;
}
void psx_irq_raise(uint32_t bit, uint32_t detail) {
(void)detail; i_stat |= 1u << bit;
}
int memcard_is_present(int card) { (void)card; return 0; }
int memcard_read_sector(int card, int sector, uint8_t *buf) {
(void)card; (void)sector; memset(buf, 0, 128); return -1;
}
int memcard_write_sector(int card, int sector, const uint8_t *buf) {
(void)card; (void)sector; (void)buf; return -1;
}
void memcard_flush(int card) { (void)card; }
#define DATA 0x1F801040u
#define STAT 0x1F801044u
#define CTRL 0x1F80104Au
#define SELECT 2u
#define TXEN 1u
#define ACK 16u
#define IRQEN 0x1000u
#define SLOT 0x2000u
static unsigned failures, checks;
static unsigned irq;
static void expect(const char *what, unsigned expected, unsigned got) {
++checks;
if (expected != got) {
fprintf(stderr, "%s: expected %X, got %X\n", what, expected, got);
++failures;
}
}
static uint8_t transfer(unsigned slot, uint8_t tx) {
const uint16_t ctrl = TXEN | SELECT | IRQEN | (slot ? SLOT : 0);
i_stat = 0;
sio_write(CTRL, ctrl);
sio_write(DATA, tx);
sio_tick(2000);
const uint8_t rx = (uint8_t)sio_read(DATA);
irq = !!(i_stat & (1u << 7));
sio_write(CTRL, ctrl | ACK);
return rx;
}
static void setup(unsigned slot) {
sio_set_multitap(0);
sio_init();
sio_connect_pad(slot);
sio_set_pad_analog(slot, SIO_PAD_DIGITAL, 0x80, 0x80, 0x80, 0x80);
sio_set_pad_config_capable(slot, 0);
sio_set_pad_state_slot(slot, 0xABCD);
}
int main(void) {
for (unsigned slot = 0; slot < 2; ++slot) {
for (unsigned cmd = 0; cmd < 256; ++cmd) {
setup(slot);
expect("address", 0xFF, transfer(slot, 0x01));
expect("address ACK", 1, irq);
expect("command ID", 0x41, transfer(slot, (uint8_t)cmd));
expect("command ACK", cmd == 0x42, irq);
if (cmd == 0x42) {
expect("poll marker", 0x5A, transfer(slot, 0));
expect("marker ACK", 1, irq);
expect("buttons low", 0xCD, transfer(slot, 0));
expect("low ACK", 1, irq);
expect("buttons high", 0xAB, transfer(slot, 0));
expect("last byte has no ACK", 0, irq);
}
}
/* Config-capable digital and analog devices keep their own replies. */
for (unsigned type = SIO_PAD_DIGITAL; type <= SIO_PAD_JOGCON; ++type) {
sio_init(); sio_connect_pad(slot);
sio_set_pad_type(slot, type, 0x80, 0x80, 0x80, 0x80);
sio_set_pad_config_capable(slot, 1);
expect("capable address", 0xFF, transfer(slot, 0x01));
expect("capable unknown command", 0xFF, transfer(slot, 0x99));
expect("capable unknown ACK", 0, irq);
}
#if PSX_MAX_PLAYERS >= 5
/* The tap's own non-poll-command behavior is deliberately unchanged. */
sio_init(); sio_set_multitap(1); sio_set_multitap_port(slot);
#if PSX_MAX_PLAYERS >= 8
const unsigned logical = slot ? 4u : 0u;
#else
const unsigned logical = slot ? 1u : 0u;
#endif
sio_connect_pad(logical);
sio_set_pad_type(logical, SIO_PAD_DIGITAL, 0x80, 0x80, 0x80, 0x80);
sio_set_pad_config_capable(logical, 0);
sio_set_pad_state_slot(logical, 0xABCD);
expect("tap poll address", 0xFF, transfer(slot, 0x01));
expect("tap poll ID", 0x41, transfer(slot, 0x42));
expect("tap poll ID ACK", 1, irq);
expect("tap poll marker", 0x5A, transfer(slot, 0));
expect("tap poll low", 0xCD, transfer(slot, 0));
expect("tap poll high", 0xAB, transfer(slot, 0));
expect("tap poll final ACK", 0, irq);
sio_write(CTRL, 0); /* New DTR session for the rejected command. */
expect("tap address", 0xFF, transfer(slot, 0x01));
expect("tap unknown command", 0xFF, transfer(slot, 0x99));
expect("tap unknown ACK", 0, irq);
#endif
/* sio_init preserves multitap preferences; return to standalone. */
sio_set_multitap(0);
/* A disconnected port still floats; do not manufacture an ID. */
sio_init();
expect("empty address", 0xFF, transfer(slot, 0x01));
expect("empty command", 0xFF, transfer(slot, 0x43));
expect("empty ACK", 0, irq);
}
printf("digital command: %u checks, %u failures\n", checks, failures);
return failures ? 1 : 0;
}