mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
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>
This commit is contained in:
@@ -29,6 +29,8 @@ set(PSX_SHARED_SOURCES
|
||||
src/psx_oracle_cmds.c
|
||||
src/psx_cycles.c
|
||||
src/starvation_ring.c
|
||||
src/card_read_summary.c
|
||||
src/card_data_writes.c
|
||||
)
|
||||
|
||||
# ---- Native target: recompiled BIOS, stub interpreter ----
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* card_data_writes.h — always-on capture of where the BIOS stores
|
||||
* incoming SIO data bytes during memory-card READ_DATA.
|
||||
*
|
||||
* The chain handler reads each byte from 0x1F801040 (SIO_RX_DATA) and
|
||||
* stores it into a destination buffer in RAM. We capture (cycle, addr,
|
||||
* value, mc_state, mc_data_idx, store_pc, func_addr) for every byte
|
||||
* that lands in RAM during a card data phase.
|
||||
*
|
||||
* Mechanism:
|
||||
* - sio_read(0x1F801040) sets a "pending" flag when mc_state is in
|
||||
* READ_DATA..READ_END and active_device==DEV_MEMCARD.
|
||||
* - The next byte/halfword/word RAM write whose new_val matches the
|
||||
* armed RX value is captured into the ring; the flag is cleared.
|
||||
* - Non-matching writes between read and store don't disarm us.
|
||||
*
|
||||
* Ring is non-evicting up to CARD_DATA_WRITES_CAP, then keeps a
|
||||
* rolling tail (so we never lose the LATEST writes). For first-32-
|
||||
* read audit, this gives ~32*128=4096 byte writes — enough to see
|
||||
* the full destination buffer for several consecutive reads.
|
||||
*/
|
||||
#ifndef PSXRECOMP_V4_CARD_DATA_WRITES_H
|
||||
#define PSXRECOMP_V4_CARD_DATA_WRITES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CARD_DATA_WRITES_CAP 4096
|
||||
|
||||
typedef struct {
|
||||
uint64_t seq; /* monotonic across all entries */
|
||||
uint64_t psx_cycle_count;
|
||||
uint32_t addr; /* dest physical RAM address */
|
||||
uint32_t store_pc; /* g_debug_last_store_pc when stored */
|
||||
uint32_t func_addr; /* g_debug_current_func_addr */
|
||||
uint32_t value; /* value written (zero-extended) */
|
||||
uint16_t mc_state_at_read; /* mc_state at the SIO read */
|
||||
uint8_t mc_data_idx_at_read; /* mc_data_idx at the SIO read (0..127) */
|
||||
uint8_t width; /* 1, 2, or 4 */
|
||||
uint8_t slot; /* mc_slot at read */
|
||||
uint8_t pad0;
|
||||
uint8_t pad1;
|
||||
uint8_t pad2;
|
||||
} CardDataWriteEntry;
|
||||
|
||||
/* Called from sio_read(0x1F801040) inside the MC_READ_DATA..READ_END
|
||||
* window. Records the byte we just delivered to BIOS so the next
|
||||
* matching RAM write can be tagged. */
|
||||
void card_data_writes_arm(uint8_t value, uint16_t mc_state,
|
||||
uint8_t mc_data_idx, uint8_t slot);
|
||||
|
||||
/* Called from psx_write_byte/half/word. If armed and value matches
|
||||
* the armed RX byte, record + disarm. Returns 1 if captured, 0
|
||||
* otherwise. */
|
||||
int card_data_writes_check(uint32_t phys, uint32_t value, uint8_t width);
|
||||
|
||||
uint32_t card_data_writes_get(const CardDataWriteEntry **buf_out,
|
||||
uint64_t *total_seq_out,
|
||||
uint32_t *head_out);
|
||||
|
||||
void card_data_writes_reset(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PSXRECOMP_V4_CARD_DATA_WRITES_H */
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* card_read_summary.h — non-evicting evidence ring for the first 32
|
||||
* completed memory-card reads.
|
||||
*
|
||||
* The card-txn ring (256 entries, evicting) covers recent activity
|
||||
* but the long history is gone by the time we audit. This ring is
|
||||
* fixed-size and STOPS recording after 32 entries, so the FIRST 32
|
||||
* successful reads post-CROSS are preserved indefinitely.
|
||||
*
|
||||
* Each entry captures the SIO-layer evidence at the MC_READ_END
|
||||
* transition: slot/cmd/sector/checksum/first 16 bytes/cycle counts/
|
||||
* caller function. The destination RAM pointer is filled in later
|
||||
* by the chain-handler trace (Phase 3).
|
||||
*/
|
||||
#ifndef PSXRECOMP_V4_CARD_READ_SUMMARY_H
|
||||
#define PSXRECOMP_V4_CARD_READ_SUMMARY_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CARD_READ_SUMMARY_CAP 32
|
||||
#define CARD_READ_SUMMARY_PEEK 16 /* bytes captured from start of buffer */
|
||||
|
||||
typedef struct {
|
||||
uint64_t seq; /* monotonic; first record = 0 */
|
||||
uint64_t psx_cycle_count; /* guest cycle clock at completion */
|
||||
uint32_t current_func; /* g_debug_current_func_addr at MC_READ_END */
|
||||
uint32_t last_store_pc; /* g_debug_last_store_pc at MC_READ_END */
|
||||
uint16_t sector;
|
||||
uint8_t slot;
|
||||
uint8_t cmd; /* mc_cmd at completion (e.g. 0x52) */
|
||||
uint8_t checksum_card; /* simulated card's computed checksum */
|
||||
uint8_t data_idx_at_end; /* should be 128 for clean READ_END */
|
||||
uint8_t pad0;
|
||||
uint8_t pad1;
|
||||
uint32_t dest_ram_addr; /* filled by chain-handler trace; 0 if unknown */
|
||||
uint8_t data_peek[CARD_READ_SUMMARY_PEEK];
|
||||
} CardReadSummary;
|
||||
|
||||
void card_read_summary_record(uint8_t slot, uint8_t cmd, uint16_t sector,
|
||||
uint8_t checksum, uint8_t data_idx,
|
||||
const uint8_t *data128);
|
||||
|
||||
/* Read access for debug_server. Returns count of recorded entries. */
|
||||
uint32_t card_read_summary_get(const CardReadSummary **buf_out);
|
||||
|
||||
/* Reset the ring (for clean audit re-runs). */
|
||||
void card_read_summary_reset(void);
|
||||
|
||||
/* Late-fill the destination RAM pointer for the most recent record.
|
||||
* Called by chain-handler trace once the BIOS commits the byte to RAM. */
|
||||
void card_read_summary_set_dest(uint32_t dest_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PSXRECOMP_V4_CARD_READ_SUMMARY_H */
|
||||
@@ -0,0 +1,73 @@
|
||||
/* 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;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* card_read_summary.c — non-evicting first-32 completed card reads. */
|
||||
|
||||
#include "card_read_summary.h"
|
||||
#include "psx_cycles.h"
|
||||
#include <string.h>
|
||||
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
|
||||
static CardReadSummary s_ring[CARD_READ_SUMMARY_CAP];
|
||||
static uint32_t s_count = 0;
|
||||
static uint64_t s_seq = 0;
|
||||
|
||||
void card_read_summary_record(uint8_t slot, uint8_t cmd, uint16_t sector,
|
||||
uint8_t checksum, uint8_t data_idx,
|
||||
const uint8_t *data128) {
|
||||
if (s_count >= CARD_READ_SUMMARY_CAP) return;
|
||||
CardReadSummary *e = &s_ring[s_count];
|
||||
memset(e, 0, sizeof(*e));
|
||||
e->seq = s_seq++;
|
||||
e->psx_cycle_count = psx_get_cycle_count();
|
||||
e->current_func = g_debug_current_func_addr;
|
||||
e->last_store_pc = g_debug_last_store_pc;
|
||||
e->slot = slot;
|
||||
e->cmd = cmd;
|
||||
e->sector = sector;
|
||||
e->checksum_card = checksum;
|
||||
e->data_idx_at_end = data_idx;
|
||||
e->dest_ram_addr = 0; /* filled by chain trace */
|
||||
if (data128) {
|
||||
memcpy(e->data_peek, data128, CARD_READ_SUMMARY_PEEK);
|
||||
}
|
||||
s_count++;
|
||||
}
|
||||
|
||||
uint32_t card_read_summary_get(const CardReadSummary **buf_out) {
|
||||
if (buf_out) *buf_out = s_ring;
|
||||
return s_count;
|
||||
}
|
||||
|
||||
void card_read_summary_reset(void) {
|
||||
memset(s_ring, 0, sizeof(s_ring));
|
||||
s_count = 0;
|
||||
s_seq = 0;
|
||||
}
|
||||
|
||||
void card_read_summary_set_dest(uint32_t dest_addr) {
|
||||
if (s_count == 0) return;
|
||||
/* Set on the most recent record (the one that just completed). */
|
||||
s_ring[s_count - 1].dest_ram_addr = dest_addr;
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "memcard.h"
|
||||
#include "interrupts.h"
|
||||
#include "psx_cycles.h"
|
||||
#include "card_read_summary.h"
|
||||
#include "card_data_writes.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -1736,6 +1738,98 @@ static void handle_card_txn_dump(int id, const char *json)
|
||||
send_fmt("],\"emitted\":%d}\n", emitted);
|
||||
}
|
||||
|
||||
static void handle_card_read_summary(int id, const char *json)
|
||||
{
|
||||
(void)json;
|
||||
const CardReadSummary *buf = NULL;
|
||||
uint32_t n = card_read_summary_get(&buf);
|
||||
|
||||
char out[16 * 1024];
|
||||
int o = snprintf(out, sizeof(out),
|
||||
"{\"id\":%d,\"ok\":true,\"count\":%u,\"cap\":%d,\"entries\":[",
|
||||
id, (unsigned)n, CARD_READ_SUMMARY_CAP);
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
const CardReadSummary *e = &buf[i];
|
||||
char peek[2 * CARD_READ_SUMMARY_PEEK + 1];
|
||||
int pi = 0;
|
||||
for (int b = 0; b < CARD_READ_SUMMARY_PEEK; b++) {
|
||||
pi += snprintf(peek + pi, sizeof(peek) - pi, "%02X", e->data_peek[b]);
|
||||
}
|
||||
peek[pi] = 0;
|
||||
o += snprintf(out + o, sizeof(out) - o,
|
||||
"%s{\"seq\":%llu,\"cyc\":%llu,"
|
||||
"\"slot\":%u,\"cmd\":\"0x%02X\",\"sector\":%u,"
|
||||
"\"checksum\":\"0x%02X\",\"data_idx\":%u,"
|
||||
"\"current_func\":\"0x%08X\",\"last_store_pc\":\"0x%08X\","
|
||||
"\"dest_ram\":\"0x%08X\",\"data_peek\":\"%s\"}",
|
||||
i == 0 ? "" : ",",
|
||||
(unsigned long long)e->seq,
|
||||
(unsigned long long)e->psx_cycle_count,
|
||||
e->slot, e->cmd, e->sector,
|
||||
e->checksum_card, e->data_idx_at_end,
|
||||
e->current_func, e->last_store_pc,
|
||||
e->dest_ram_addr, peek);
|
||||
if (o >= (int)sizeof(out) - 512) break;
|
||||
}
|
||||
snprintf(out + o, sizeof(out) - o, "]}");
|
||||
send_fmt("%s", out);
|
||||
}
|
||||
|
||||
static void handle_card_read_summary_reset(int id, const char *json)
|
||||
{
|
||||
(void)json;
|
||||
card_read_summary_reset();
|
||||
send_fmt("{\"id\":%d,\"ok\":true}\n", id);
|
||||
}
|
||||
|
||||
static void handle_card_data_writes(int id, const char *json)
|
||||
{
|
||||
int count = json_get_int(json, "count", 256);
|
||||
if (count < 1) count = 1;
|
||||
if (count > CARD_DATA_WRITES_CAP) count = CARD_DATA_WRITES_CAP;
|
||||
|
||||
const CardDataWriteEntry *buf = NULL;
|
||||
uint64_t total_seq = 0;
|
||||
uint32_t head = 0;
|
||||
uint32_t avail = card_data_writes_get(&buf, &total_seq, &head);
|
||||
if ((uint32_t)count > avail) count = (int)avail;
|
||||
|
||||
char out[64 * 1024];
|
||||
int o = snprintf(out, sizeof(out),
|
||||
"{\"id\":%d,\"ok\":true,\"total_seq\":%llu,"
|
||||
"\"avail\":%u,\"count\":%d,\"entries\":[",
|
||||
id, (unsigned long long)total_seq, avail, count);
|
||||
|
||||
/* Iterate the OLDEST `count` entries first (reading from
|
||||
* (head - avail) forward, then taking the last `count`). */
|
||||
int start = ((int)head - count + (int)CARD_DATA_WRITES_CAP) % (int)CARD_DATA_WRITES_CAP;
|
||||
for (int i = 0; i < count; i++) {
|
||||
int idx = (start + i) % (int)CARD_DATA_WRITES_CAP;
|
||||
const CardDataWriteEntry *e = &buf[idx];
|
||||
o += snprintf(out + o, sizeof(out) - o,
|
||||
"%s{\"seq\":%llu,\"cyc\":%llu,\"addr\":\"0x%08X\","
|
||||
"\"value\":\"0x%08X\",\"width\":%u,"
|
||||
"\"mc_state\":%u,\"mc_idx\":%u,\"slot\":%u,"
|
||||
"\"store_pc\":\"0x%08X\",\"func\":\"0x%08X\"}",
|
||||
i == 0 ? "" : ",",
|
||||
(unsigned long long)e->seq,
|
||||
(unsigned long long)e->psx_cycle_count,
|
||||
e->addr, e->value, e->width,
|
||||
e->mc_state_at_read, e->mc_data_idx_at_read,
|
||||
e->slot, e->store_pc, e->func_addr);
|
||||
if (o >= (int)sizeof(out) - 512) break;
|
||||
}
|
||||
snprintf(out + o, sizeof(out) - o, "]}");
|
||||
send_fmt("%s", out);
|
||||
}
|
||||
|
||||
static void handle_card_data_writes_reset(int id, const char *json)
|
||||
{
|
||||
(void)json;
|
||||
card_data_writes_reset();
|
||||
send_fmt("{\"id\":%d,\"ok\":true}\n", id);
|
||||
}
|
||||
|
||||
static void handle_watch(int id, const char *json)
|
||||
{
|
||||
char addr_str[32];
|
||||
@@ -2886,6 +2980,10 @@ static const CmdEntry s_commands[] = {
|
||||
{ "sio_pc_trace", handle_sio_pc_trace },
|
||||
{ "dirty_ram_stats", handle_dirty_ram_stats },
|
||||
{ "card_txn_dump", handle_card_txn_dump },
|
||||
{ "card_read_summary", handle_card_read_summary },
|
||||
{ "card_read_summary_reset", handle_card_read_summary_reset },
|
||||
{ "card_data_writes", handle_card_data_writes },
|
||||
{ "card_data_writes_reset", handle_card_data_writes_reset },
|
||||
{ "sio_irq_dump", handle_sio_irq_dump },
|
||||
{ "evcb_snapshot", handle_evcb_snapshot },
|
||||
{ "evcb_walk_dump", handle_evcb_walk_dump },
|
||||
|
||||
@@ -140,6 +140,9 @@ extern void debug_server_trace_write_check(uint32_t phys, uint32_t old_val,
|
||||
uint32_t new_val, uint8_t width);
|
||||
extern void debug_server_trace_mmio_write(uint32_t addr, uint32_t val, uint8_t width);
|
||||
|
||||
/* Card-byte destination capture (Phase 3 audit). Always-on. */
|
||||
extern int card_data_writes_check(uint32_t phys, uint32_t value, uint8_t width);
|
||||
|
||||
static inline uint32_t read_ram_word(uint32_t phys) {
|
||||
return (uint32_t)ram[phys]
|
||||
| ((uint32_t)ram[phys + 1] << 8)
|
||||
@@ -470,6 +473,7 @@ void psx_write_word(uint32_t addr, uint32_t val) {
|
||||
|
||||
if (phys < RAM_SIZE) {
|
||||
debug_server_trace_write_check(phys, read_ram_word(phys), val, 4);
|
||||
card_data_writes_check(phys, val, 4);
|
||||
dirty_ram_mark(phys);
|
||||
ram[phys] = (uint8_t)(val);
|
||||
ram[phys + 1] = (uint8_t)(val >> 8);
|
||||
@@ -527,6 +531,7 @@ void psx_write_half(uint32_t addr, uint16_t val) {
|
||||
|
||||
if (phys < RAM_SIZE) {
|
||||
debug_server_trace_write_check(phys, (uint32_t)read_ram_half(phys), (uint32_t)val, 2);
|
||||
card_data_writes_check(phys, (uint32_t)val, 2);
|
||||
dirty_ram_mark(phys);
|
||||
ram[phys] = (uint8_t)(val);
|
||||
ram[phys + 1] = (uint8_t)(val >> 8);
|
||||
@@ -576,6 +581,7 @@ void psx_write_byte(uint32_t addr, uint8_t val) {
|
||||
|
||||
if (phys < RAM_SIZE) {
|
||||
debug_server_trace_write_check(phys, (uint32_t)ram[phys], (uint32_t)val, 1);
|
||||
card_data_writes_check(phys, (uint32_t)val, 1);
|
||||
dirty_ram_mark(phys);
|
||||
ram[phys] = val;
|
||||
return;
|
||||
|
||||
+18
-1
@@ -708,15 +708,23 @@ static void mc_process_byte(uint8_t tx_byte) {
|
||||
sio_stat |= SIO_STAT_ACK;
|
||||
break;
|
||||
|
||||
case MC_READ_END:
|
||||
case MC_READ_END: {
|
||||
mc_state = MC_IDLE;
|
||||
sio_mc_read_done++;
|
||||
extern void card_read_summary_record(uint8_t slot, uint8_t cmd,
|
||||
uint16_t sector,
|
||||
uint8_t checksum,
|
||||
uint8_t data_idx,
|
||||
const uint8_t *data128);
|
||||
card_read_summary_record(mc_slot, mc_cmd, mc_sector,
|
||||
mc_checksum, mc_data_idx, mc_data);
|
||||
if (mc_sector < MEMCARD_SECTORS) {
|
||||
sio_rx_data = 0x47; /* 'G' = Good */
|
||||
} else {
|
||||
sio_rx_data = 0xFF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* ---- WRITE states ---- */
|
||||
case MC_WRITE_DATA:
|
||||
@@ -966,6 +974,15 @@ uint32_t sio_read(uint32_t addr) {
|
||||
uint8_t b = sio_rx_data;
|
||||
sio_stat &= ~SIO_STAT_RX_RDY;
|
||||
sr_record(SR_EVT_RX_DATA_READ, 0, b);
|
||||
if (active_device == DEV_MEMCARD &&
|
||||
mc_state >= MC_READ_DATA && mc_state <= MC_READ_END) {
|
||||
extern void card_data_writes_arm(uint8_t value,
|
||||
uint16_t mc_state,
|
||||
uint8_t mc_data_idx,
|
||||
uint8_t slot);
|
||||
card_data_writes_arm(b, (uint16_t)mc_state,
|
||||
mc_data_idx, (uint8_t)mc_slot);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
"""Print first 16 bytes of each on-disk card image."""
|
||||
for path in ('dummy.0.mcr', 'dummy.1.mcr', 'card1.mcd', 'card2.mcd'):
|
||||
try:
|
||||
d = open(path, 'rb').read()[:16]
|
||||
print(f'{path:>15}: ' + ' '.join(f'{b:02X}' for b in d))
|
||||
except Exception as ex:
|
||||
print(f'{path}: ERR {ex}')
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Dump the card-read summary ring (first 32 successful reads, non-evicting)."""
|
||||
import socket, json, sys
|
||||
|
||||
host, port = '127.0.0.1', 4370
|
||||
s = socket.create_connection((host, port))
|
||||
s.settimeout(3.0)
|
||||
s.sendall(json.dumps({'id': 1, 'cmd': 'card_read_summary'}).encode() + b'\n')
|
||||
|
||||
buf = b''
|
||||
while True:
|
||||
try:
|
||||
chunk = s.recv(65536)
|
||||
except socket.timeout:
|
||||
break
|
||||
if not chunk:
|
||||
break
|
||||
buf += chunk
|
||||
depth = 0; in_str = False; esc = False
|
||||
for c in buf:
|
||||
if esc:
|
||||
esc = False; continue
|
||||
if c == 0x5C:
|
||||
esc = True; continue
|
||||
if c == 0x22:
|
||||
in_str = not in_str; continue
|
||||
if in_str:
|
||||
continue
|
||||
if c == 0x7B:
|
||||
depth += 1
|
||||
elif c == 0x7D:
|
||||
depth -= 1
|
||||
if depth == 0 and buf.strip():
|
||||
break
|
||||
s.close()
|
||||
|
||||
obj = json.loads(buf.decode())
|
||||
print(f"# count={obj['count']} cap={obj['cap']}")
|
||||
for e in obj.get('entries', []):
|
||||
print(f"#{e['seq']:>2} cyc={e['cyc']:>14} slot={e['slot']} cmd={e['cmd']} sec={e['sector']:>5} "
|
||||
f"chk={e['checksum']} idx={e['data_idx']:>3} func={e['current_func']} pc={e['last_store_pc']} "
|
||||
f"dest={e['dest_ram']} peek={e['data_peek']}")
|
||||
@@ -0,0 +1,73 @@
|
||||
"""Dump the card-data-writes ring (BIOS RAM destinations of incoming card bytes).
|
||||
|
||||
Each entry: (seq, cyc, addr, value, width, mc_state, mc_idx, slot, store_pc, func).
|
||||
The mc_state values: 8=READ_DATA, 9=READ_CHK, 10=READ_END (or whatever your enum says).
|
||||
mc_idx is 0..127 — the offset within the 128-byte sector that this byte represents.
|
||||
"""
|
||||
import socket, json, sys
|
||||
|
||||
count = int(sys.argv[1]) if len(sys.argv) > 1 else 1024
|
||||
host, port = '127.0.0.1', 4370
|
||||
s = socket.create_connection((host, port))
|
||||
s.settimeout(3.0)
|
||||
s.sendall(json.dumps({'id': 1, 'cmd': 'card_data_writes', 'count': count}).encode() + b'\n')
|
||||
|
||||
buf = b''
|
||||
while True:
|
||||
try:
|
||||
chunk = s.recv(65536)
|
||||
except socket.timeout:
|
||||
break
|
||||
if not chunk:
|
||||
break
|
||||
buf += chunk
|
||||
depth = 0; in_str = False; esc = False
|
||||
for c in buf:
|
||||
if esc:
|
||||
esc = False; continue
|
||||
if c == 0x5C:
|
||||
esc = True; continue
|
||||
if c == 0x22:
|
||||
in_str = not in_str; continue
|
||||
if in_str:
|
||||
continue
|
||||
if c == 0x7B:
|
||||
depth += 1
|
||||
elif c == 0x7D:
|
||||
depth -= 1
|
||||
if depth == 0 and buf.strip():
|
||||
break
|
||||
s.close()
|
||||
|
||||
obj = json.loads(buf.decode())
|
||||
print(f"# total_seq={obj['total_seq']} avail={obj['avail']} count={obj['count']}")
|
||||
|
||||
# Group consecutive entries by destination buffer base.
|
||||
entries = obj.get('entries', [])
|
||||
if not entries:
|
||||
print("# no entries")
|
||||
sys.exit(0)
|
||||
|
||||
# Show summary: dest base inferred from each run of mc_idx 0..127.
|
||||
i = 0
|
||||
while i < len(entries):
|
||||
run = [entries[i]]
|
||||
j = i + 1
|
||||
while j < len(entries):
|
||||
# consecutive idx OR new run starts at idx 0
|
||||
prev = run[-1]
|
||||
cur = entries[j]
|
||||
if cur['mc_idx'] == prev['mc_idx'] + 1 and cur['slot'] == prev['slot']:
|
||||
run.append(cur)
|
||||
j += 1
|
||||
else:
|
||||
break
|
||||
base = run[0]
|
||||
end = run[-1]
|
||||
base_addr = int(base['addr'], 16)
|
||||
end_addr = int(end['addr'], 16)
|
||||
print(f"slot={base['slot']} idx_range=[{base['mc_idx']}..{end['mc_idx']}] "
|
||||
f"addr=0x{base_addr:08X}..0x{end_addr:08X} ({len(run)} bytes) "
|
||||
f"first_func={base['func']} first_pc={base['store_pc']} "
|
||||
f"first_val={base['value']}")
|
||||
i = j
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Dump card-data-writes ring entry-by-entry."""
|
||||
import socket, json, sys
|
||||
|
||||
count = int(sys.argv[1]) if len(sys.argv) > 1 else 200
|
||||
host, port = '127.0.0.1', 4370
|
||||
s = socket.create_connection((host, port))
|
||||
s.settimeout(3.0)
|
||||
s.sendall(json.dumps({'id': 1, 'cmd': 'card_data_writes', 'count': count}).encode() + b'\n')
|
||||
buf = b''
|
||||
while True:
|
||||
try: chunk = s.recv(65536)
|
||||
except socket.timeout: break
|
||||
if not chunk: break
|
||||
buf += chunk
|
||||
depth = 0; in_str = False; esc = False
|
||||
for c in buf:
|
||||
if esc: esc = False; continue
|
||||
if c == 0x5C: esc = True; continue
|
||||
if c == 0x22: in_str = not in_str; continue
|
||||
if in_str: continue
|
||||
if c == 0x7B: depth += 1
|
||||
elif c == 0x7D: depth -= 1
|
||||
if depth == 0 and buf.strip(): break
|
||||
s.close()
|
||||
obj = json.loads(buf.decode())
|
||||
print(f"# total_seq={obj['total_seq']} avail={obj['avail']} count={obj['count']}")
|
||||
for e in obj.get('entries', []):
|
||||
val_lo = int(e['value'], 16) & 0xFF
|
||||
print(f"#{e['seq']:>4} cyc={e['cyc']:>14} slot={e['slot']} idx={e['mc_idx']:>3} "
|
||||
f"st={e['mc_state']:>2} addr={e['addr']} val=0x{val_lo:02X} w={e['width']} "
|
||||
f"pc={e['store_pc']} func={e['func']}")
|
||||
Reference in New Issue
Block a user