Files
psxrecomp/runtime/src/card_data_writes.c
T
Matthew Stanley 1b59103b3c runtime: card-read summary + card-data-writes diagnostic rings
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>
2026-04-29 22:20:55 -07:00

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;
}