mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 18:25:35 -04:00
1b59103b3c
Two passive, always-on rings for auditing memory-card reads end-to-end without arming/timing probes: card_read_summary (non-evicting, first 32 successful reads): Captured at the MC_READ_END transition in sio_process_byte. Records slot/cmd/sector/checksum/data_idx/first 16 bytes/cycle/caller func. TCP cmds: card_read_summary, card_read_summary_reset. card_data_writes (4096 entries, rolling tail): Arm side: sio_read(SIO_RX_DATA) sets a pending flag while the card protocol is in MC_READ_DATA..MC_READ_END. Check side: psx_write_byte/half/word capture the next RAM write whose new_val matches the armed RX byte, then disarm. Records dest addr / value / store_pc / func / mc_idx / slot / cycle. TCP cmds: card_data_writes, card_data_writes_reset. Helpers: tools/probe_card_reads.py tools/probe_card_writes.py tools/probe_card_writes_raw.py tools/peek_cards.py Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
74 lines
2.5 KiB
C
74 lines
2.5 KiB
C
/* card_data_writes.c — capture BIOS RAM destination of card data bytes. */
|
|
|
|
#include "card_data_writes.h"
|
|
#include "psx_cycles.h"
|
|
#include <string.h>
|
|
|
|
extern uint32_t g_debug_current_func_addr;
|
|
extern uint32_t g_debug_last_store_pc;
|
|
|
|
/* "Armed" byte from the most recent SIO_RX_DATA read inside a card
|
|
* data phase. The next matching RAM write captures it. */
|
|
static volatile int s_armed = 0;
|
|
static uint8_t s_armed_value = 0;
|
|
static uint16_t s_armed_state = 0;
|
|
static uint8_t s_armed_idx = 0;
|
|
static uint8_t s_armed_slot = 0;
|
|
|
|
/* Ring (rolling: keeps last CAP entries; total_seq lets clients see
|
|
* how many were dropped). */
|
|
static CardDataWriteEntry s_ring[CARD_DATA_WRITES_CAP];
|
|
static uint64_t s_seq = 0;
|
|
static uint32_t s_head = 0;
|
|
|
|
void card_data_writes_arm(uint8_t value, uint16_t mc_state,
|
|
uint8_t mc_data_idx, uint8_t slot) {
|
|
s_armed_value = value;
|
|
s_armed_state = mc_state;
|
|
s_armed_idx = mc_data_idx;
|
|
s_armed_slot = slot;
|
|
s_armed = 1;
|
|
}
|
|
|
|
int card_data_writes_check(uint32_t phys, uint32_t value, uint8_t width) {
|
|
if (!s_armed) return 0;
|
|
/* Match on the byte value zero-extended. For width==1, compare bottom
|
|
* byte. For wider, compare bottom byte of stored value. */
|
|
uint8_t low = (uint8_t)(value & 0xFF);
|
|
if (low != s_armed_value) return 0;
|
|
|
|
CardDataWriteEntry *e = &s_ring[s_head];
|
|
e->seq = s_seq++;
|
|
e->psx_cycle_count = psx_get_cycle_count();
|
|
e->addr = phys;
|
|
e->store_pc = g_debug_last_store_pc;
|
|
e->func_addr = g_debug_current_func_addr;
|
|
e->value = value;
|
|
e->mc_state_at_read = s_armed_state;
|
|
e->mc_data_idx_at_read = s_armed_idx;
|
|
e->width = width;
|
|
e->slot = s_armed_slot;
|
|
s_head = (s_head + 1) % CARD_DATA_WRITES_CAP;
|
|
|
|
s_armed = 0;
|
|
return 1;
|
|
}
|
|
|
|
uint32_t card_data_writes_get(const CardDataWriteEntry **buf_out,
|
|
uint64_t *total_seq_out,
|
|
uint32_t *head_out) {
|
|
if (buf_out) *buf_out = s_ring;
|
|
if (total_seq_out) *total_seq_out = s_seq;
|
|
if (head_out) *head_out = s_head;
|
|
uint32_t avail = (s_seq < CARD_DATA_WRITES_CAP)
|
|
? (uint32_t)s_seq : CARD_DATA_WRITES_CAP;
|
|
return avail;
|
|
}
|
|
|
|
void card_data_writes_reset(void) {
|
|
memset(s_ring, 0, sizeof(s_ring));
|
|
s_seq = 0;
|
|
s_head = 0;
|
|
s_armed = 0;
|
|
}
|