Compare commits

...

3 Commits

2 changed files with 192 additions and 70 deletions

View File

@ -0,0 +1,7 @@
; @language: Z80 ASM
; Simple possible code test:
; outputs a single character "A" on IO port 0, then halts
ld a,'A'
out (1),a
halt

View File

@ -1,18 +1,19 @@
#include <Z/constants/pointer.h> /* Z_NULL */
#include <Z80.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define CYCLES_PER_FRAME 69888
#define CYCLES_AT_INT 24
#define CYCLES_PER_INT 32
#define ROM_SIZE 0x8000 /* 32 KiB */
#define MEMORY_SIZE 0x8000 /* 32 KiB */
#define MEMORY_SIZE 0xFFFF /* 64 KiB */
typedef struct {
void* context;
zuint8 (* read)(void *context);
void (* write)(void *context, zuint8 value);
zuint16 assigned_port;
} Device;
typedef struct {
@ -24,49 +25,107 @@ typedef struct {
} Machine;
Device *machine_find_device(Machine *self, zuint16 port)
{
zusize index = 0;
for (; index < self->device_count; index++)
if (self->devices[index].assigned_port == port)
return &self->devices[index];
return Z_NULL;
}
static zuint8 machine_cpu_read(Machine *self, zuint16 address)
{
static zuint8 machine_cpu_read(Machine *self, zuint16 address) {
return address < MEMORY_SIZE ? self->memory[address] : 0xFF;
}
}
static void machine_cpu_write(Machine *self, zuint16 address, zuint8 value)
{
static void machine_cpu_write(Machine *self, zuint16 address, zuint8 value) {
if (address >= ROM_SIZE && address < MEMORY_SIZE)
self->memory[address] = value;
}
static zuint8 machine_cpu_in(Machine *self, zuint16 port) {
// Pat80 has 8 devices, decoded based on the 3 most significant IO addr bits.
// Note the Z80 has 16 bit address bus, but only the first 8 are used as IO addr,
// so the 3 most significant IO addr bits in this case are A7, A6, A5. The bits
// A4-A0 may be used by the single device, at its own discretion.
zuint16 bitmask = 7; // 0000000000000111
int decoded = port & bitmask;
if (decoded <= 0x1F) {
// Port 0 (0x00 to 0x1F): terminal
//printf("TERMINAL_READ");
//return 'H';
return 0x00;
}
static zuint8 machine_cpu_in(Machine *self, zuint16 port)
{
Device *device = machine_find_device(self, port);
return device != Z_NULL ? device->read(device->context) : 0xFF;
if (decoded <= 0x3F) {
// Port 1 (0x20 to 0x3F): sound card (sn76489)
printf("\nsound_cmd[IN]: Not supported!\n");
return 0x00;
}
static void machine_cpu_out(Machine *self, zuint16 port, zuint8 value)
{
Device *device = machine_find_device(self, port);
if (device != Z_NULL) device->write(device->context, value);
if (decoded <= 0x5F) {
// Port 2 (0x40 to 0x5F)
printf("IO_ERROR_IN: No device at port 2\n");
return 0x00;
}
if (decoded <= 0x7F) {
// Port 3 (0x60 to 0x7F)
printf("IO_ERROR_IN: No device at port 3\n");
return 0x00;
}
if (decoded <= 0x9F) {
// Port 4 (0x80 to 0x9F)
printf("IO_ERROR_IN: No device at port 4\n");
return 0x00;
}
if (decoded <= 0x5F) {
// Port 5 (0xA0 to 0xBF)
printf("IO_ERROR_IN: No device at port 5\n");
return 0x00;
}
if (decoded <= 0x5F) {
// Port 6 (0xC0 to 0xDF)
printf("IO_ERROR_IN: No device at port 6\n");
return 0x00;
}
if (decoded <= 0x5F) {
// Port 7 (0xE0 to 0xFF)
printf("IO_ERROR_IN: No device at port 7\n");
} else {
printf("IO_ERROR_IN: Invalid port address: %#04x\n", port);
}
}
void machine_initialize(Machine *self)
{
static void machine_cpu_out(Machine *self, zuint16 port, zuint8 value) {
// Pat80 has 8 devices, decoded based on the 3 most significant IO addr bits.
// Note the Z80 has 16 bit address bus, but only the first 8 are used as IO addr,
// so the 3 most significant IO addr bits in this case are A7, A6, A5. The bits
// A4-A0 may be used by the single device, at its own discretion.
zuint16 bitmask = 0xE0; // 0000000011100000
int decoded = port & bitmask;
if (decoded <= 0x1F) {
// Port 0 (0x00 to 0x1F): terminal
printf("%c", value);
} else if (decoded <= 0x3F) {
// Port 1 (0x20 to 0x3F): sound card (sn76489)
printf("\nsound_cmd[%#04x]\n", value);
} else if (decoded <= 0x5F) {
// Port 2 (0x40 to 0x5F)
printf("IO_ERROR_OUT: No device at port 2\n");
} else if (decoded <= 0x7F) {
// Port 3 (0x60 to 0x7F)
printf("IO_ERROR_OUT: No device at port 3\n");
} else if (decoded <= 0x9F) {
// Port 4 (0x80 to 0x9F)
printf("IO_ERROR_OUT: No device at port 4\n");
} else if (decoded <= 0x5F) {
// Port 5 (0xA0 to 0xBF)
printf("IO_ERROR_OUT: No device at port 5\n");
} else if (decoded <= 0x5F) {
// Port 6 (0xC0 to 0xDF)
printf("IO_ERROR_OUT: No device at port 6\n");
} else if (decoded <= 0x5F) {
// Port 7 (0xE0 to 0xFF)
printf("IO_ERROR_OUT: No device at port 7\n");
} else {
printf("IO_ERROR_OUT: Invalid port address: %#04x\n", port);
}
}
void machine_initialize(Machine *self) {
self->cpu.context = self;
self->cpu.fetch_opcode =
self->cpu.fetch =
@ -88,58 +147,114 @@ void machine_initialize(Machine *self)
self->cpu.options = Z80_MODEL_ZILOG_NMOS;
/* Create and initialize devices... */
}
}
void machine_power(Machine *self, zbool state)
{
void machine_power(Machine *self, zbool state) {
if (state)
{
self->cycles = 0;
memset(self->memory, 0, 65536);
memset(self->memory, 0, MEMORY_SIZE);
}
z80_power(&self->cpu, state);
}
}
void machine_reset(Machine *self)
{
void machine_reset(Machine *self) {
z80_instant_reset(&self->cpu);
}
void machine_run(Machine *self) {
z80_run(&self->cpu, Z80_MAXIMUM_CYCLES);
}
// void machine_run_frame(Machine *self)
// {
// /* CPU cycles before the INT signal */
// self->cycles += z80_execute(&self->cpu, CYCLES_AT_INT - self->cycles);
// /* CPU cycles during the INT signal */
// z80_int(&self->cpu, Z_TRUE);
// self->cycles += z80_run(&self->cpu, (CYCLES_AT_INT + CYCLES_PER_INT) - self->cycles);
// z80_int(&self->cpu, Z_FALSE);
// /* CPU cycles after the INT signal */
// self->cycles += z80_execute(&self->cpu, CYCLES_PER_FRAME - self->cycles);
// self->cycles -= CYCLES_PER_FRAME;
// }
// TEST IMPLEM DEVICE
zuint8 device_terminal_read(void *context) {
return 'H';
//return 0;
}
void device_terminal_write(void *context, zuint8 value) {
printf("%c", value);
}
zuint8 device_sound_read(void *context) {
return 0;
}
void device_sound_write(void *context, zuint8 value) {
printf("sound[%c]\n", value);
}
// TEST IMPLEM DEVICE
int main(int argc, char *argv[]) {
// Parse arguments
if (argc < 2) {
printf("Usage: %s [romFile]\n", argv[0]);
exit(0);
}
char* romFilePath = argv[1];
void machine_run_frame(Machine *self)
{
/* CPU cycles before the INT signal */
self->cycles += z80_execute(&self->cpu, CYCLES_AT_INT - self->cycles);
/* CPU cycles during the INT signal */
z80_int(&self->cpu, Z_TRUE);
self->cycles += z80_run(&self->cpu, (CYCLES_AT_INT + CYCLES_PER_INT) - self->cycles);
z80_int(&self->cpu, Z_FALSE);
/* CPU cycles after the INT signal */
self->cycles += z80_execute(&self->cpu, CYCLES_PER_FRAME - self->cycles);
self->cycles -= CYCLES_PER_FRAME;
}
int main() {
// Setup virtual Pat80 computer
Z80 pat80Cpu = {};
Device pat80Devices[] = {};
zuint8 pat80Memory[65536] = {0};
Machine pat80 = {
/*zusize*/ .cycles = 0,
/*zuint8*/ .memory = *pat80Memory,
/*Z80*/ .cpu = pat80Cpu,
/*Device**/ .devices = pat80Devices,
/*zusize*/ .device_count = 255
/*zusize*/ .cycles = 100000,
/*Z80*/ .cpu = pat80Cpu
};
machine_initialize(&pat80);
machine_power(&pat80, Z_TRUE);
// Load ROM into memory
FILE *romFile;
romFile = fopen(romFilePath,"rb");
if (romFile == NULL) {
printf("Unable to open rom file at %s", romFilePath);
exit(1);
}
fread(&pat80.memory,ROM_SIZE,1,romFile); // load rom from file into memory, up >
fclose(romFile);
// Declare system devices
Device patoTerminal = {
.read = *device_terminal_read,
.write = *device_terminal_write,
};
Device patoSound = {
.read = *device_sound_read,
.write = *device_sound_write,
};
Device patoDevices[2] = {
patoTerminal, // Port 0 (0x00 to 0x1F)
patoSound // Port 1 (0x20 to 0x3F)
};
pat80.devices = patoDevices;
pat80.device_count = 2;
machine_reset(&pat80);
}
machine_run(&pat80);
}