#include "sim-main.h"
#include "hw-main.h"
enum { NR_VECTORS = 7, };
struct mn103cpu_block {
unsigned_word base;
unsigned_word bound;
};
struct mn103cpu {
struct mn103cpu_block block;
struct hw_event *pending_handler;
int pending_level;
int pending_nmi;
int pending_reset;
unsigned16 interrupt_vector[NR_VECTORS];
unsigned16 internal_memory_control;
unsigned16 cpu_mode;
};
enum {
RESET_PORT,
NMI_PORT,
LEVEL_PORT,
};
enum {
ACK_PORT,
};
static const struct hw_port_descriptor mn103cpu_ports[] = {
{ "reset", RESET_PORT, 0, input_port, },
{ "nmi", NMI_PORT, 0, input_port, },
{ "level", LEVEL_PORT, 0, input_port, },
{ "ack", ACK_PORT, 0, output_port, },
{ NULL, },
};
static hw_io_read_buffer_method mn103cpu_io_read_buffer;
static hw_io_write_buffer_method mn103cpu_io_write_buffer;
static hw_port_event_method mn103cpu_port_event;
static void
attach_mn103cpu_regs (struct hw *me,
struct mn103cpu *controller)
{
unsigned_word attach_address;
int attach_space;
unsigned attach_size;
reg_property_spec reg;
if (hw_find_property (me, "reg") == NULL)
hw_abort (me, "Missing \"reg\" property");
if (!hw_find_reg_array_property (me, "reg", 0, ®))
hw_abort (me, "\"reg\" property must contain three addr/size entries");
hw_unit_address_to_attach_address (hw_parent (me),
®.address,
&attach_space,
&attach_address,
me);
controller->block.base = attach_address;
hw_unit_size_to_attach_size (hw_parent (me),
®.size,
&attach_size, me);
controller->block.bound = attach_address + (attach_size - 1);
if ((controller->block.base & 3) != 0)
hw_abort (me, "cpu register block must be 4 byte aligned");
hw_attach_address (hw_parent (me),
0,
attach_space, attach_address, attach_size,
me);
}
static void
mn103cpu_finish (struct hw *me)
{
struct mn103cpu *controller;
controller = HW_ZALLOC (me, struct mn103cpu);
set_hw_data (me, controller);
set_hw_io_read_buffer (me, mn103cpu_io_read_buffer);
set_hw_io_write_buffer (me, mn103cpu_io_write_buffer);
set_hw_ports (me, mn103cpu_ports);
set_hw_port_event (me, mn103cpu_port_event);
attach_mn103cpu_regs (me, controller);
controller->pending_level = 7;
}
static void
deliver_mn103cpu_interrupt (struct hw *me,
void *data)
{
struct mn103cpu *controller = hw_data (me);
SIM_DESC simulator = hw_system (me);
sim_cpu *cpu = STATE_CPU (simulator, 0);
if (controller->pending_reset)
{
controller->pending_reset = 0;
HW_TRACE ((me, "Reset!"));
hw_abort (me, "Reset!");
}
else if (controller->pending_nmi)
{
controller->pending_nmi = 0;
store_word (SP - 4, CIA_GET (cpu));
store_half (SP - 8, PSW);
PSW &= ~PSW_IE;
SP = SP - 8;
CIA_SET (cpu, 0x40000008);
HW_TRACE ((me, "nmi pc=0x%08lx psw=0x%04x sp=0x%08lx",
(long) CIA_GET (cpu), (unsigned) PSW, (long) SP));
}
else if ((controller->pending_level < EXTRACT_PSW_LM)
&& (PSW & PSW_IE))
{
store_word (SP - 4, CIA_GET (cpu));
store_half (SP - 8, PSW);
PSW &= ~PSW_IE;
PSW &= ~PSW_LM;
PSW |= INSERT_PSW_LM (controller->pending_level);
SP = SP - 8;
CIA_SET (cpu, 0x40000000 + controller->interrupt_vector[controller->pending_level]);
HW_TRACE ((me, "port-out ack %d", controller->pending_level));
hw_port_event (me, ACK_PORT, controller->pending_level);
HW_TRACE ((me, "int level=%d pc=0x%08lx psw=0x%04x sp=0x%08lx",
controller->pending_level,
(long) CIA_GET (cpu), (unsigned) PSW, (long) SP));
}
if (controller->pending_level < 7)
{
if (controller->pending_handler != NULL)
controller->pending_handler =
hw_event_queue_schedule (me, 1, deliver_mn103cpu_interrupt, NULL);
}
else
{
controller->pending_handler = NULL;
}
}
static void
mn103cpu_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
struct mn103cpu *controller = hw_data (me);
if (controller->pending_handler == NULL)
controller->pending_handler =
hw_event_queue_schedule (me, 0, deliver_mn103cpu_interrupt, NULL);
switch (my_port)
{
case RESET_PORT:
controller->pending_reset = 1;
HW_TRACE ((me, "port-in reset"));
break;
case NMI_PORT:
controller->pending_nmi = 1;
HW_TRACE ((me, "port-in nmi"));
break;
case LEVEL_PORT:
controller->pending_level = level;
HW_TRACE ((me, "port-in level=%d", level));
break;
default:
hw_abort (me, "bad switch");
break;
}
}
enum mn103cpu_regs {
INVALID_REG,
IVR0_REG,
IVR1_REG,
IVR2_REG,
IVR3_REG,
IVR4_REG,
IVR5_REG,
IVR6_REG,
IMCR_REG,
CPUM_REG,
};
static enum mn103cpu_regs
decode_mn103cpu_addr (struct hw *me,
struct mn103cpu *controller,
unsigned_word base)
{
switch (base - controller->block.base)
{
case 0x000: return IVR0_REG;
case 0x004: return IVR1_REG;
case 0x008: return IVR2_REG;
case 0x00c: return IVR3_REG;
case 0x010: return IVR4_REG;
case 0x014: return IVR5_REG;
case 0x018: return IVR6_REG;
case 0x020: return IMCR_REG;
case 0x040: return CPUM_REG;
default: return INVALID_REG;
}
}
static unsigned
mn103cpu_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
struct mn103cpu *controller = hw_data (me);
unsigned16 val = 0;
enum mn103cpu_regs reg = decode_mn103cpu_addr (me, controller, base);
switch (reg)
{
case IVR0_REG:
case IVR1_REG:
case IVR2_REG:
case IVR3_REG:
case IVR4_REG:
case IVR5_REG:
case IVR6_REG:
val = controller->interrupt_vector[reg - IVR0_REG];
break;
case IMCR_REG:
val = controller->internal_memory_control;
break;
case CPUM_REG:
val = controller->cpu_mode;
break;
default:
break;
}
if (nr_bytes == 2)
*(unsigned16*) dest = H2LE_2 (val);
return nr_bytes;
}
static unsigned
mn103cpu_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
struct mn103cpu *controller = hw_data (me);
unsigned16 val;
enum mn103cpu_regs reg;
if (nr_bytes != 2)
hw_abort (me, "must be two byte write");
reg = decode_mn103cpu_addr (me, controller, base);
val = LE2H_2 (* (unsigned16 *) source);
switch (reg)
{
case IVR0_REG:
case IVR1_REG:
case IVR2_REG:
case IVR3_REG:
case IVR4_REG:
case IVR5_REG:
case IVR6_REG:
controller->interrupt_vector[reg - IVR0_REG] = val;
HW_TRACE ((me, "ivr%d = 0x%04lx", reg - IVR0_REG, (long) val));
break;
default:
break;
}
return nr_bytes;
}
const struct hw_descriptor dv_mn103cpu_descriptor[] = {
{ "mn103cpu", mn103cpu_finish, },
{ NULL },
};