1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-14 15:28:05 +11:00
collapseos/emul/z80/emul.c
Virgil Dupras efe4b13a4e Move /emul to /emul/z80
I'm planning on adding other subfolders. 8086 for example...
2020-10-24 16:50:22 -04:00

199 lines
4.2 KiB
C

/* Common code between forth and stage binaries.
They all run on the same kind of virtual machine: A z80 CPU, 64K of RAM/ROM.
*/
#include <string.h>
#include "emul.h"
// Port for block reads. Each read or write has to be done in 5 IO writes:
// 1 - r/w. 1 for read, 2 for write.
// 2 - blkid MSB
// 3 - blkid LSB
// 4 - dest addr MSB
// 5 - dest addr LSB
#define BLK_PORT 0x03
#ifndef BLKFS_PATH
#error BLKFS_PATH needed
#endif
#ifndef FBIN_PATH
#error FBIN_PATH needed
#endif
static Machine m;
static ushort traceval = 0;
static uint64_t blkop = 0; // 5 bytes
static FILE *blkfp;
static uint8_t io_read(int unused, uint16_t addr)
{
addr &= 0xff;
IORD fn = m.iord[addr];
if (fn != NULL) {
return fn();
} else {
fprintf(stderr, "Out of bounds I/O read: %d\n", addr);
return 0;
}
}
static void io_write(int unused, uint16_t addr, uint8_t val)
{
addr &= 0xff;
IOWR fn = m.iowr[addr];
if (fn != NULL) {
fn(val);
} else {
fprintf(stderr, "Out of bounds I/O write: %d / %d (0x%x)\n", addr, val, val);
}
}
static void iowr_blk(uint8_t val)
{
blkop <<= 8;
blkop |= val;
uint8_t rw = blkop >> 32;
if (rw) {
uint16_t blkid = (blkop >> 16);
uint16_t dest = blkop & 0xffff;
blkop = 0;
fseek(blkfp, blkid*1024, SEEK_SET);
if (rw==2) { // write
fwrite(&m.mem[dest], 1024, 1, blkfp);
} else { // read
fread(&m.mem[dest], 1024, 1, blkfp);
}
}
}
static uint8_t mem_read(int unused, uint16_t addr)
{
return m.mem[addr];
}
static void mem_write(int unused, uint16_t addr, uint8_t val)
{
if (addr < m.ramstart) {
fprintf(stderr, "Writing to ROM (%d)!\n", addr);
emul_memdump();
fprintf(stderr, "Press any key to continue...\n");
while (getchar() > 0x100);
}
m.mem[addr] = val;
}
Machine* emul_init()
{
fprintf(stderr, "Using blkfs %s\n", BLKFS_PATH);
blkfp = fopen(BLKFS_PATH, "r+");
if (!blkfp) {
fprintf(stderr, "Can't open\n");
return NULL;
}
fseek(blkfp, 0, SEEK_END);
if (ftell(blkfp) < 100 * 1024) {
fclose(blkfp);
fprintf(stderr, "emul/blkfs too small, something's wrong, aborting.\n");
return NULL;
}
fseek(blkfp, 0, SEEK_SET);
// initialize memory
memset(m.mem, 0, 0x10000);
FILE *bfp = fopen(FBIN_PATH, "r");
if (!bfp) {
fprintf(stderr, "Can't open forth.bin\n");
return NULL;
}
int i = 0;
int c = getc(bfp);
while (c != EOF) {
m.mem[i++] = c;
c = getc(bfp);
}
fclose(bfp);
m.ramstart = 0;
m.minsp = 0xffff;
m.maxix = 0;
for (int i=0; i<0x100; i++) {
m.iord[i] = NULL;
m.iowr[i] = NULL;
}
Z80RESET(&m.cpu);
m.cpu.memRead = mem_read;
m.cpu.memWrite = mem_write;
m.cpu.ioRead = io_read;
m.cpu.ioWrite = io_write;
m.iowr[BLK_PORT] = iowr_blk;
return &m;
}
void emul_deinit()
{
fclose(blkfp);
}
bool emul_step()
{
if (!m.cpu.halted) {
Z80Execute(&m.cpu);
ushort newsp = m.cpu.R1.wr.SP;
if (newsp != 0 && newsp < m.minsp) {
m.minsp = newsp;
}
if (m.cpu.R1.wr.IX > m.maxix) {
m.maxix = m.cpu.R1.wr.IX;
}
return true;
} else {
return false;
}
}
bool emul_steps(unsigned int steps)
{
while (steps) {
if (!emul_step()) {
return false;
}
steps--;
}
return true;
}
void emul_loop()
{
while (emul_step());
}
void emul_trace(ushort addr)
{
ushort newval = m.mem[addr+1] << 8 | m.mem[addr];
if (newval != traceval) {
traceval = newval;
fprintf(stderr, "trace: %04x PC: %04x\n", traceval, m.cpu.PC);
}
}
void emul_memdump()
{
fprintf(stderr, "Dumping memory to memdump. PC %04x\n", m.cpu.PC);
FILE *fp = fopen("memdump", "w");
fwrite(m.mem, 0x10000, 1, fp);
fclose(fp);
}
void emul_debugstr(char *s)
{
sprintf(s, "SP %04x (%04x) IX %04x (%04x)",
m.cpu.R1.wr.SP, m.minsp, m.cpu.R1.wr.IX, m.maxix);
}
void emul_printdebug()
{
fprintf(stderr, "Min SP: %04x\n", m.minsp);
fprintf(stderr, "Max IX: %04x\n", m.maxix);
}
byte iord_noop() { return 0; }
void iowr_noop(byte val) {}