#include "z80e.h"
#include "ops.h"
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
Z80E *z80e_new() {
Z80E *cpu = calloc(1, sizeof(Z80E));
if (cpu) cpu->sp = sizeof(cpu->memory) - 1;
return cpu;
}
void z80e_destroy(Z80E *cpu) {
z80e_watched_mem_t *mnode = cpu->watched_mem;
while (mnode) {
z80e_watched_mem_t *next = mnode->next;
free(mnode);
mnode = next;
}
z80e_device_t *dnode = cpu->devices;
while (dnode) {
z80e_device_t *next = dnode->next;
free(dnode);
dnode = next;
}
free(cpu);
}
int z80e_step(Z80E *cpu) {
z80e_watched_mem_t *watched_mem;
// Save the bytes from memory addresses that are being watched.
watched_mem = cpu->watched_mem;
while (watched_mem) {
watched_mem->old_value = cpu->memory[watched_mem->address];
watched_mem = watched_mem->next;
}
// Fetch the next opcode and execute its corresponding function.
uint8_t opcode = cpu->memory[cpu->pc++];
int t_states = Z80E_OPCODES[opcode](cpu, opcode);
// Execute callbacks if any watched memory has changed.
watched_mem = cpu->watched_mem;
while (watched_mem) {
if (watched_mem->old_value != cpu->memory[watched_mem->address])
((z80e_watched_mem_cb*) watched_mem->callback)(cpu, watched_mem);
watched_mem = watched_mem->next;
}
// Check for interrupts.
if (cpu->nmi) {
cpu->nmi = 0;
cpu->iff = cpu->iff << 1 & 2;
cpu->memory[--cpu->sp] = cpu->pc >> 8 & 0x00ff;
cpu->memory[--cpu->sp] = cpu->pc & 0x00ff;
cpu->pc = 0x0066;
} else if (cpu->iff & 1) {
z80e_device_t *node = cpu->devices;
while (node) {
if (node->interrupt) {
switch (cpu->im) {
case 0:
// TODO
break;
case 1:
cpu->iff = 0;
cpu->memory[--cpu->sp] = cpu->pc >> 8 & 0x00ff;
cpu->memory[--cpu->sp] = cpu->pc & 0x00ff;
cpu->pc = 0x0038;
break;
case 2:
// TODO
break;
}
if (node->iorq_cb)
((z80e_iorq_cb*) node->iorq_cb)(cpu, node->io_addr);
else
node->interrupt = 0;
break;
}
node = node->next;
}
}
return t_states;
}
z80e_stop_reason_t z80e_run(Z80E *cpu, int cycles) {
cpu->halt = 0;
while (cycles > 0) {
cycles -= z80e_step(cpu);
if (cpu->halt) return Z80E_HALT;
}
return Z80E_CYCLES;
}
void z80e_interrupt(Z80E *cpu, uint8_t io_addr) {
z80e_device_t *node = cpu->devices;
while (node) {
if (node->io_addr == io_addr) {
node->interrupt = 1;
break;
}
node = node->next;
}
}
void z80e_clr_interrupt(Z80E *cpu, uint8_t io_addr) {
z80e_device_t *node = cpu->devices;
while (node) {
if (node->io_addr == io_addr) {
node->interrupt = 0;
break;
}
node = node->next;
}
}
void z80e_nmi(Z80E *cpu, uint8_t io_addr) {
cpu->nmi = 1;
}
int z80e_connect_io(Z80E *cpu, uint8_t io_addr, z80e_iowrite_cb *write_cb,
z80e_ioread_cb *read_cb, z80e_iorq_cb *iorq_cb) {
z80e_device_t *new_node;
new_node = calloc(1, sizeof(z80e_device_t));
if (!new_node) return -1;
new_node->io_addr = io_addr;
new_node->write_cb = write_cb;
new_node->read_cb = read_cb;
new_node->iorq_cb = iorq_cb;
if (cpu->devices) {
z80e_device_t *node = cpu->devices;
while (node->next) node = node->next;
node->next = new_node;
} else {
cpu->devices = new_node;
}
return 0;
}
void z80e_disconnect_io(Z80E *cpu, uint8_t io_addr) {
z80e_device_t *node = cpu->devices;
z80e_device_t *prev_node = NULL;
while (node) {
if (node->io_addr == io_addr) {
if (prev_node)
prev_node->next = node->next;
else
cpu->devices = node->next;
free(node);
break;
}
prev_node = node;
node = node->next;
}
}
int z80e_watch_mem(Z80E *cpu, uint16_t addr, z80e_watched_mem_cb *callbk) {
// Allocate a blank watched memory structure.
z80e_watched_mem_t *new_node;
new_node = calloc(1, sizeof(z80e_watched_mem_t));
if (!new_node) return -1;
// Fill in the watched memory structure.
new_node->address = addr;
new_node->callback = callbk;
// Add the structure to the emulator's list of watched memory.
if (cpu->watched_mem) {
z80e_watched_mem_t *node = cpu->watched_mem;
while (node->next) node = node->next;
node->next = new_node;
} else {
cpu->watched_mem = new_node;
}
return 0;
}
void z80e_unwatch_mem(Z80E *cpu, uint16_t addr) {
z80e_watched_mem_t *node = cpu->watched_mem;
z80e_watched_mem_t *prev_node = NULL;
while (node) {
if (node->address == addr) {
if (prev_node)
prev_node->next = node->next;
else
cpu->watched_mem = node->next;
free(node);
break;
}
prev_node = node;
node = node->next;
}
}
int z80e_mirror(Z80E *cpu, uint16_t base1, uint16_t base2, uint16_t length) {
// Make sure the two blocks don't overlap.
uint16_t last1 = base1 + length;
uint16_t last2 = base2 + length;
if ((base2 >= base1 && base2 <= last1) ||
(last2 >= base1 && last2 <= last1) ||
(base1 >= base2 && base1 <= last2) ||
(last1 >= base2 && last1 <= last2))
return -1;
// Make sure neither block extends beyond the end of addressable memory.
if ((((uint32_t) base1) + ((uint32_t) length) > 0xffff) ||
(((uint32_t) base2) + ((uint32_t) length) > 0xffff))
return -1;
// Range check the length.
if (length == 0) return -1;
// Set up the mirror and copy block 2 to block 1.
cpu->mirror_base[0] = base1;
cpu->mirror_base[1] = base2;
cpu->mirror_len = length;
memcpy(&cpu->memory[base1], &cpu->memory[base2], length);
return 0;
}
void z80e_no_mirror(Z80E *cpu) {
cpu->mirror_base[0] = cpu->mirror_base[1] = cpu->mirror_len = 0;
}
float z80e_max_clock() {
struct timeval start_time, end_time, elapsed_time;
Z80E *cpu;
cpu = z80e_new();
cpu->memory[0xffff] = 0x76;
gettimeofday(&start_time, NULL);
z80e_run(cpu, 1000000);
gettimeofday(&end_time, NULL);
z80e_destroy(cpu);
timersub(&end_time, &start_time, &elapsed_time);
// 262144 is the number of T states (clock cycles) it takes to run 65535 NOPs
// and one HALT.
return 262144.0 / (elapsed_time.tv_usec + elapsed_time.tv_sec * 1000000);
}