2019-12-02 16:35:49 -05:00
|
|
|
/* Common code between shell, zasm and runbin.
|
|
|
|
|
|
|
|
They all run on the same kind of virtual machine: A z80 CPU, 64K of RAM/ROM.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include "emul.h"
|
|
|
|
|
|
|
|
static Machine m;
|
2020-03-29 22:13:54 -04:00
|
|
|
static ushort traceval = 0;
|
2019-12-02 16:35:49 -05:00
|
|
|
|
|
|
|
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 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);
|
2020-04-09 08:26:41 -04:00
|
|
|
emul_memdump();
|
|
|
|
fprintf(stderr, "Press any key to continue...\n");
|
|
|
|
while (getchar() > 0x100);
|
2019-12-02 16:35:49 -05:00
|
|
|
}
|
|
|
|
m.mem[addr] = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
Machine* emul_init()
|
|
|
|
{
|
|
|
|
memset(m.mem, 0, 0x10000);
|
|
|
|
m.ramstart = 0;
|
2019-12-02 17:44:54 -05:00
|
|
|
m.minsp = 0xffff;
|
2020-05-14 18:41:09 -04:00
|
|
|
m.maxix = 0;
|
2019-12-02 16:35:49 -05:00
|
|
|
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;
|
|
|
|
return &m;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool emul_step()
|
|
|
|
{
|
|
|
|
if (!m.cpu.halted) {
|
|
|
|
Z80Execute(&m.cpu);
|
2019-12-02 17:44:54 -05:00
|
|
|
ushort newsp = m.cpu.R1.wr.SP;
|
|
|
|
if (newsp != 0 && newsp < m.minsp) {
|
|
|
|
m.minsp = newsp;
|
|
|
|
}
|
2020-05-14 18:41:09 -04:00
|
|
|
if (m.cpu.R1.wr.IX > m.maxix) {
|
|
|
|
m.maxix = m.cpu.R1.wr.IX;
|
|
|
|
}
|
2019-12-02 16:35:49 -05:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-01 22:48:01 -05:00
|
|
|
bool emul_steps(unsigned int steps)
|
|
|
|
{
|
|
|
|
while (steps) {
|
|
|
|
if (!emul_step()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
steps--;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-12-02 16:35:49 -05:00
|
|
|
void emul_loop()
|
|
|
|
{
|
|
|
|
while (emul_step());
|
|
|
|
}
|
2019-12-02 17:44:54 -05:00
|
|
|
|
2020-03-29 22:13:54 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-09 08:26:41 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-12-02 17:44:54 -05:00
|
|
|
void emul_printdebug()
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Min SP: %04x\n", m.minsp);
|
2020-05-14 18:41:09 -04:00
|
|
|
fprintf(stderr, "Max IX: %04x\n", m.maxix);
|
2019-12-02 17:44:54 -05:00
|
|
|
}
|