velxio/test/test_intel/test_buses/rom-1m.c

125 lines
3.8 KiB
C

/*
* rom-1m — top-of-1MB ROM custom chip for the 8086.
*
* Naming is historical; the chip is actually a 64 KB ROM mapped at
* physical addresses 0xF0000..0xFFFFF — the upper 64 KB of the 8086's
* 1 MiB address space, which is where real-world PC BIOSes sit. This
* fits within the WASM 1 MiB linear-memory cap with room for the chip's
* other state.
*
* The chip listens on the full 20-bit address bus (A0..A19); when the
* upper 4 address bits are not 0xF, the chip releases the data bus
* (out-of-range — let another chip drive). Reset vector 0xFFFF0 maps
* to image offset 0xFFF0.
*
* Pin contract:
* A0..A19 input 20-bit address
* D0..D7 output 8-bit data (driven only when CE̅=0 AND OE̅=0
* AND addr is in [0xF0000..0xFFFFF])
* CE̅ input active-low chip enable
* OE̅ input active-low output enable
* VCC, GND power
*
* Image is allocated via malloc at chip_setup. A small known signature
* is patched at the reset vector for tests to verify ROM presence.
*
* For per-demo ROM contents, a follow-up SDK extension (blob attribute)
* would let users upload arbitrary boot images. For now each "ROM
* image" is a separately compiled chip variant.
*/
#include "velxio-chip.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#define ROM_BASE 0xF0000
#define ROM_SIZE 0x10000 /* 64 KB */
#define ROM_END (ROM_BASE + ROM_SIZE)
typedef struct {
vx_pin a[20];
vx_pin d[8];
vx_pin ce;
vx_pin oe;
vx_pin vcc, gnd;
uint8_t* image;
bool driving;
} chip_t;
static chip_t G;
static uint32_t read_addr(void) {
uint32_t v = 0;
for (int i = 0; i < 20; i++) if (vx_pin_read(G.a[i])) v |= (1u << i);
return v;
}
static void drive_data(uint8_t v) {
for (int i = 0; i < 8; i++) {
vx_pin_set_mode(G.d[i], VX_OUTPUT);
vx_pin_write(G.d[i], (v >> i) & 1);
}
G.driving = true;
}
static void release_data(void) {
if (!G.driving) return;
for (int i = 0; i < 8; i++) vx_pin_set_mode(G.d[i], VX_INPUT);
G.driving = false;
}
static void update(void) {
int ce_low = (vx_pin_read(G.ce) == 0);
int oe_low = (vx_pin_read(G.oe) == 0);
if (!ce_low || !oe_low) { release_data(); return; }
uint32_t addr = read_addr();
if (addr < ROM_BASE || addr >= ROM_END) { release_data(); return; }
drive_data(G.image[addr - ROM_BASE]);
}
static void on_pin_change(void* user_data, vx_pin pin, int value) {
(void)user_data; (void)pin; (void)value;
update();
}
void chip_setup(void) {
char name[5];
for (int i = 0; i < 20; i++) {
if (i < 10) {
name[0]='A'; name[1]='0'+i; name[2]=0;
} else {
name[0]='A'; name[1]='1'; name[2]='0'+(i-10); name[3]=0;
}
G.a[i] = vx_pin_register(name, VX_INPUT);
}
for (int i = 0; i < 8; i++) {
name[0]='D'; name[1]='0'+i; name[2]=0;
G.d[i] = vx_pin_register(name, VX_INPUT);
}
G.ce = vx_pin_register("CE", VX_INPUT);
G.oe = vx_pin_register("OE", VX_INPUT);
G.vcc = vx_pin_register("VCC", VX_INPUT);
G.gnd = vx_pin_register("GND", VX_INPUT);
G.image = (uint8_t*)malloc(ROM_SIZE);
memset(G.image, 0xFF, ROM_SIZE);
/* Test fixture: 16-byte signature at the reset vector 0xFFFF0,
which maps to image offset 0xFFF0. */
static const uint8_t reset_signature[16] = {
0xEA, 0x00, 0x01, 0x00, 0xF0, /* JMP FAR 0xF000:0x0100 */
0x55, 0xAA, 0x12, 0x34,
0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x77,
};
memcpy(&G.image[0xFFF0], reset_signature, sizeof reset_signature);
G.driving = false;
for (int i = 0; i < 20; i++) {
vx_pin_watch(G.a[i], VX_EDGE_BOTH, on_pin_change, 0);
}
vx_pin_watch(G.ce, VX_EDGE_BOTH, on_pin_change, 0);
vx_pin_watch(G.oe, VX_EDGE_BOTH, on_pin_change, 0);
update();
}