2017-01-16 18:35:28 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
2018-03-23 12:37:07 +00:00
|
|
|
#include <errno.h>
|
2017-01-16 18:35:28 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "bus.h"
|
2017-01-17 13:29:20 +00:00
|
|
|
#include "instruction.h"
|
|
|
|
#include "rk65c02.h"
|
2018-03-21 15:22:29 +00:00
|
|
|
#include "log.h"
|
2017-02-14 10:17:40 +00:00
|
|
|
#include "debug.h"
|
2017-01-16 18:35:28 +00:00
|
|
|
|
2017-01-26 12:11:00 +00:00
|
|
|
void rk65c02_exec(rk65c02emu_t *);
|
|
|
|
|
2017-01-27 09:13:32 +00:00
|
|
|
/*
|
|
|
|
* Prepare the emulator for use, set initial CPU state.
|
|
|
|
*/
|
2017-01-18 16:18:19 +00:00
|
|
|
rk65c02emu_t
|
|
|
|
rk65c02_init(bus_t *b)
|
|
|
|
{
|
|
|
|
rk65c02emu_t e;
|
|
|
|
|
|
|
|
e.bus = b;
|
|
|
|
e.state = STOPPED;
|
2017-02-09 20:53:45 +00:00
|
|
|
e.stopreason = HOST;
|
2017-01-25 20:17:18 +00:00
|
|
|
e.regs.P = P_UNDEFINED|P_IRQ_DISABLE;
|
2017-01-30 23:31:04 +00:00
|
|
|
/* reset also clears the decimal flag */
|
|
|
|
e.regs.P &= ~P_DECIMAL;
|
2017-01-18 16:18:19 +00:00
|
|
|
|
2017-02-04 20:44:13 +00:00
|
|
|
e.irq = false;
|
|
|
|
|
2017-02-14 10:17:40 +00:00
|
|
|
e.bps_head = NULL;
|
2017-02-15 20:32:12 +00:00
|
|
|
e.trace_head = NULL;
|
2017-02-15 20:38:51 +00:00
|
|
|
e.runtime_disassembly = false;
|
2017-02-14 10:17:40 +00:00
|
|
|
|
2018-03-23 12:37:07 +00:00
|
|
|
rk65c02_log(LOG_DEBUG, "Initialized new emulator.");
|
2018-03-21 15:22:29 +00:00
|
|
|
|
2017-01-18 16:18:19 +00:00
|
|
|
return e;
|
|
|
|
}
|
2017-01-16 18:35:28 +00:00
|
|
|
|
2017-02-04 20:44:13 +00:00
|
|
|
/*
|
2017-02-09 20:53:45 +00:00
|
|
|
* Assert the IRQ line.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rk65c02_assert_irq(rk65c02emu_t *e)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Clearly this is too simpleton'ish, because more than one device
|
|
|
|
* might want to assert the interrupt line (on hardware level it is
|
|
|
|
* active low, so can just be pulled down by any device connected
|
|
|
|
* to it.
|
|
|
|
*/
|
|
|
|
e->irq = true;
|
|
|
|
|
|
|
|
if ((e->state == STOPPED) && (e->stopreason == WAI))
|
|
|
|
rk65c02_start(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Respond to interrupt and start the interrupt service routine.
|
2017-02-04 20:44:13 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
rk65c02_irq(rk65c02emu_t *e)
|
|
|
|
{
|
|
|
|
/* push return address to the stack */
|
|
|
|
stack_push(e, e->regs.PC >> 8);
|
|
|
|
stack_push(e, e->regs.PC & 0xFF);
|
|
|
|
/* push processor status to the stack with break flag set */
|
|
|
|
stack_push(e, e->regs.P);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The IRQ disable is set, decimal flags is cleared _after_ pushing
|
|
|
|
* the P register to the stack.
|
|
|
|
*/
|
|
|
|
e->regs.P |= P_IRQ_DISABLE;
|
|
|
|
e->regs.P &= ~P_DECIMAL;
|
|
|
|
|
|
|
|
/* break flag is only saved to the stack, it is not present in the ISR... */
|
|
|
|
e->regs.P &= ~P_BREAK;
|
|
|
|
|
|
|
|
/* load address from IRQ vector into program counter */
|
|
|
|
e->regs.PC = bus_read_1(e->bus, VECTOR_IRQ);
|
|
|
|
e->regs.PC |= bus_read_1(e->bus, VECTOR_IRQ + 1) << 8;
|
|
|
|
}
|
|
|
|
|
2017-01-27 09:13:32 +00:00
|
|
|
/*
|
|
|
|
* Execute a single instruction within emulator.
|
|
|
|
*/
|
2017-01-16 18:35:28 +00:00
|
|
|
void
|
2017-01-26 12:11:00 +00:00
|
|
|
rk65c02_exec(rk65c02emu_t *e)
|
|
|
|
{
|
2017-01-16 18:35:28 +00:00
|
|
|
instruction_t i;
|
2017-01-18 16:18:19 +00:00
|
|
|
instrdef_t id;
|
2017-02-15 20:32:12 +00:00
|
|
|
uint16_t tpc; /* saved PC for tracing */
|
|
|
|
|
|
|
|
tpc = e->regs.PC;
|
2017-01-16 18:35:28 +00:00
|
|
|
|
2017-02-04 20:44:13 +00:00
|
|
|
if (e->irq && (!(e->regs.P & P_IRQ_DISABLE)))
|
|
|
|
rk65c02_irq(e);
|
|
|
|
|
2017-02-14 10:17:40 +00:00
|
|
|
/* XXX: handle watchpoints toos */
|
|
|
|
if (debug_PC_is_breakpoint(e)) {
|
|
|
|
e->state = STOPPED;
|
|
|
|
e->stopreason = BREAKPOINT;
|
2017-02-15 18:56:22 +00:00
|
|
|
return;
|
2017-02-14 10:17:40 +00:00
|
|
|
}
|
2017-01-26 12:11:00 +00:00
|
|
|
|
2017-02-15 20:38:51 +00:00
|
|
|
if (e->runtime_disassembly)
|
|
|
|
disassemble(e->bus, e->regs.PC);
|
2017-01-26 12:11:00 +00:00
|
|
|
|
|
|
|
i = instruction_fetch(e->bus, e->regs.PC);
|
|
|
|
id = instruction_decode(i.opcode);
|
|
|
|
|
|
|
|
if (id.emul != NULL) {
|
|
|
|
id.emul(e, &id, &i);
|
2017-02-15 20:32:12 +00:00
|
|
|
|
2017-01-27 19:43:08 +00:00
|
|
|
if (!instruction_modify_pc(&id))
|
|
|
|
program_counter_increment(e, &id);
|
2017-01-26 12:11:00 +00:00
|
|
|
} else {
|
2018-03-23 12:37:07 +00:00
|
|
|
rk65c02_log(LOG_ERROR, "unimplemented opcode %X @ %X\n",
|
2018-03-22 14:08:51 +00:00
|
|
|
i.opcode, e->regs.PC);
|
2017-01-26 12:11:00 +00:00
|
|
|
e->state = STOPPED;
|
|
|
|
e->stopreason = EMUERROR;
|
|
|
|
}
|
2017-02-15 20:32:12 +00:00
|
|
|
|
|
|
|
if (e->trace)
|
|
|
|
debug_trace_savestate(e, tpc, &id, &i);
|
|
|
|
|
2017-01-26 12:11:00 +00:00
|
|
|
}
|
|
|
|
|
2017-01-27 09:13:32 +00:00
|
|
|
/*
|
|
|
|
* Start the emulator.
|
|
|
|
*/
|
2017-01-26 12:11:00 +00:00
|
|
|
void
|
|
|
|
rk65c02_start(rk65c02emu_t *e) {
|
|
|
|
|
2017-02-02 13:43:44 +00:00
|
|
|
assert(e != NULL);
|
|
|
|
|
2017-01-18 16:18:19 +00:00
|
|
|
e->state = RUNNING;
|
|
|
|
while (e->state == RUNNING) {
|
2017-01-26 12:11:00 +00:00
|
|
|
rk65c02_exec(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-27 09:13:32 +00:00
|
|
|
/*
|
|
|
|
* Execute as many instructions as specified in steps argument.
|
|
|
|
*/
|
2017-01-26 12:11:00 +00:00
|
|
|
void
|
|
|
|
rk65c02_step(rk65c02emu_t *e, uint16_t steps) {
|
|
|
|
|
|
|
|
uint16_t i = 0;
|
|
|
|
|
2017-02-02 13:43:44 +00:00
|
|
|
assert(e != NULL);
|
|
|
|
|
2017-01-26 12:11:00 +00:00
|
|
|
e->state = STEPPING;
|
|
|
|
while ((e->state == STEPPING) && (i < steps)) {
|
|
|
|
rk65c02_exec(e);
|
|
|
|
i++;
|
2017-01-16 18:35:28 +00:00
|
|
|
}
|
2017-02-03 21:21:43 +00:00
|
|
|
|
|
|
|
e->state = STOPPED;
|
|
|
|
e->stopreason = STEPPED;
|
2017-01-16 18:35:28 +00:00
|
|
|
}
|
2017-01-27 10:27:14 +00:00
|
|
|
|
2017-02-06 22:16:00 +00:00
|
|
|
void
|
|
|
|
rk65c02_dump_stack(rk65c02emu_t *e, uint8_t n)
|
|
|
|
{
|
|
|
|
uint16_t stackaddr;
|
|
|
|
|
|
|
|
stackaddr = STACK_END-n;
|
|
|
|
|
|
|
|
while (stackaddr <= STACK_END) {
|
|
|
|
|
|
|
|
if ((stackaddr == STACK_END-n) || !((stackaddr % 0x10)))
|
|
|
|
printf("stack %#02x: ", stackaddr);
|
|
|
|
|
|
|
|
printf("%#02x ", bus_read_1(e->bus, stackaddr));
|
|
|
|
|
|
|
|
stackaddr++;
|
|
|
|
|
|
|
|
if (!(stackaddr % 0x10))
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-27 10:27:14 +00:00
|
|
|
void
|
2017-02-15 20:32:12 +00:00
|
|
|
rk65c02_dump_regs(reg_state_t regs)
|
2017-01-27 10:27:14 +00:00
|
|
|
{
|
2018-03-23 12:37:07 +00:00
|
|
|
char *str;
|
|
|
|
|
|
|
|
str = rk65c02_regs_string_get(regs);
|
|
|
|
|
|
|
|
printf ("%s", str);
|
|
|
|
|
|
|
|
free(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
rk65c02_regs_string_get(reg_state_t regs)
|
|
|
|
{
|
|
|
|
#define REGS_STR_LEN 50
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
/* XXX: string allocation to a separate utility function? */
|
|
|
|
str = malloc(REGS_STR_LEN);
|
|
|
|
if (str == NULL) {
|
|
|
|
rk65c02_log(LOG_CRIT, "Error allocating memory for buffer: %s.",
|
|
|
|
strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memset(str, 0, REGS_STR_LEN);
|
|
|
|
|
|
|
|
snprintf(str, REGS_STR_LEN, "A: %X X: %X Y: %X PC: %X SP: %X P: %c%c%c%c%c%c%c%c",
|
|
|
|
regs.A, regs.X, regs.Y, regs.PC, regs.SP,
|
|
|
|
(regs.P & P_NEGATIVE) ? 'N' : '-',
|
|
|
|
(regs.P & P_SIGN_OVERFLOW) ? 'V' : '-',
|
|
|
|
(regs.P & P_UNDEFINED) ? '1' : '-',
|
|
|
|
(regs.P & P_BREAK) ? 'B' : '-',
|
|
|
|
(regs.P & P_DECIMAL) ? 'D' : '-',
|
|
|
|
(regs.P & P_IRQ_DISABLE) ? 'I' : '-',
|
|
|
|
(regs.P & P_ZERO) ? 'Z' : '-',
|
|
|
|
(regs.P & P_CARRY) ? 'C' : '-');
|
|
|
|
|
|
|
|
return str;
|
2017-01-27 10:27:14 +00:00
|
|
|
}
|
2017-01-16 22:54:24 +00:00
|
|
|
/*
|
2017-01-16 18:35:28 +00:00
|
|
|
int
|
|
|
|
main(void)
|
|
|
|
{
|
|
|
|
bus_t b;
|
|
|
|
|
|
|
|
b = bus_init();
|
|
|
|
|
|
|
|
bus_write_1(&b, 0, OP_INX);
|
|
|
|
bus_write_1(&b, 1, OP_NOP);
|
|
|
|
bus_write_1(&b, 2, OP_LDY_IMM);
|
|
|
|
bus_write_1(&b, 3, 0x1);
|
|
|
|
bus_write_1(&b, 4, OP_TSB_ZP);
|
|
|
|
bus_write_1(&b, 5, 0x3);
|
|
|
|
bus_write_1(&b, 6, OP_JSR);
|
|
|
|
bus_write_1(&b, 7, 0x09);
|
|
|
|
bus_write_1(&b, 8, 0x0);
|
|
|
|
bus_write_1(&b, 9, OP_STP);
|
|
|
|
|
2018-03-23 12:37:07 +00:00
|
|
|
rk65c02_start(&b, 0);
|
2017-01-16 18:35:28 +00:00
|
|
|
|
|
|
|
bus_finish(&b);
|
|
|
|
}
|
2017-01-16 22:54:24 +00:00
|
|
|
*/
|