/**************************************************************************** * real mode i286 emulator v1.4 by Fabrice Frances * * (initial work based on David Hedley's pcemu) * ****************************************************************************/ /* 26.March 2000 PeT changed set_irq_line */ #include "debugger.h" #include "cpuintrf.h" #include "host.h" #include "i86priv.h" #include "i86.h" #include "i86mem.h" extern int i386_dasm_one(char *buffer, UINT32 eip, const UINT8 *oprom, int mode); #define VERBOSE 0 #define LOG(x) do { if (VERBOSE) mame_printf_debug x; } while (0) /* All pre-i286 CPUs have a 1MB address space */ #define AMASK 0xfffff /* I86 registers */ typedef union { /* eight general registers */ UINT16 w[8]; /* viewed as 16 bits registers */ UINT8 b[16]; /* or as 8 bit registers */ } i8086basicregs; typedef struct _i8086_state i8086_state; struct _i8086_state { i8086basicregs regs; UINT32 pc; UINT32 prevpc; UINT32 base[4]; UINT16 sregs[4]; UINT16 flags; cpu_irq_callback irq_callback; INT32 AuxVal, OverVal, SignVal, ZeroVal, CarryVal, DirVal; /* 0 or non-0 valued flags */ UINT8 ParityVal; UINT8 TF, IF; /* 0 or 1 valued flags */ UINT8 MF; /* V30 mode flag */ UINT8 int_vector; INT8 nmi_state; INT8 irq_state; INT8 test_state; /* PJB 03/05 */ UINT8 rep_in_progress; INT32 extra_cycles; /* extra cycles for interrupts */ UINT16 ip; UINT32 sp; memory_interface mem; const device_config *device; const address_space *program; const address_space *io; int icount; cpu_state_table state; unsigned prefix_base; /* base address of the latest prefix segment */ char seg_prefix; /* prefix segment indicator */ unsigned ea; UINT16 eo; /* HJB 12/13/98 effective offset of the address (before segment is added) */ }; INLINE i8086_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == CPU); assert(cpu_get_type(device) == CPU_I8086 || cpu_get_type(device) == CPU_I8088 || cpu_get_type(device) == CPU_I80186 || cpu_get_type(device) == CPU_I80188); return (i8086_state *)device->token; } /*************************************************************************** CPU STATE DESCRIPTION ***************************************************************************/ #define I86_STATE_ENTRY(_name, _format, _member, _datamask, _flags) \ CPU_STATE_ENTRY(I8086_##_name, #_name, _format, i8086_state, _member, _datamask, ~0, _flags) static const cpu_state_entry state_array[] = { I86_STATE_ENTRY(GENPC, "%9s", pc, 0xfffff, CPUSTATE_IMPORT) I86_STATE_ENTRY(GENPCBASE, "%08X", pc, 0xfffff, CPUSTATE_NOSHOW) /* not implemented */ I86_STATE_ENTRY(IP, "%04X", ip, 0xffff, CPUSTATE_IMPORT | CPUSTATE_EXPORT) I86_STATE_ENTRY(FLAGS, "%04X", flags, 0xffff, CPUSTATE_NOSHOW | CPUSTATE_IMPORT | CPUSTATE_EXPORT) I86_STATE_ENTRY(AX, "%04X", regs.w[AX], 0xffff, 0) I86_STATE_ENTRY(BX, "%04X", regs.w[BX], 0xffff, 0) I86_STATE_ENTRY(CX, "%04X", regs.w[CX], 0xffff, 0) I86_STATE_ENTRY(DX, "%04X", regs.w[DX], 0xffff, 0) I86_STATE_ENTRY(SI, "%04X", regs.w[SI], 0xffff, 0) I86_STATE_ENTRY(DI, "%04X", regs.w[DI], 0xffff, 0) I86_STATE_ENTRY(BP, "%04X", regs.w[BP], 0xffff, 0) I86_STATE_ENTRY(SP, "%04X", regs.w[SP], 0xffff, 0) I86_STATE_ENTRY(GENSP, "%9s", sp, 0xfffff, CPUSTATE_IMPORT | CPUSTATE_EXPORT) I86_STATE_ENTRY(AL, "%02X", regs.b[AL], 0xff, CPUSTATE_NOSHOW) I86_STATE_ENTRY(BL, "%02X", regs.b[BL], 0xff, CPUSTATE_NOSHOW) I86_STATE_ENTRY(CL, "%02X", regs.b[CL], 0xff, CPUSTATE_NOSHOW) I86_STATE_ENTRY(DL, "%02X", regs.b[DL], 0xff, CPUSTATE_NOSHOW) I86_STATE_ENTRY(AH, "%02X", regs.b[AH], 0xff, CPUSTATE_NOSHOW) I86_STATE_ENTRY(BH, "%02X", regs.b[BH], 0xff, CPUSTATE_NOSHOW) I86_STATE_ENTRY(CH, "%02X", regs.b[CH], 0xff, CPUSTATE_NOSHOW) I86_STATE_ENTRY(DH, "%02X", regs.b[DH], 0xff, CPUSTATE_NOSHOW) I86_STATE_ENTRY(CS, "%04X", sregs[CS], 0xffff, CPUSTATE_IMPORT) I86_STATE_ENTRY(DS, "%04X", sregs[DS], 0xffff, CPUSTATE_IMPORT) I86_STATE_ENTRY(ES, "%04X", sregs[ES], 0xffff, CPUSTATE_IMPORT) I86_STATE_ENTRY(SS, "%04X", sregs[SS], 0xffff, CPUSTATE_IMPORT) }; static const cpu_state_table state_table_template = { NULL, /* pointer to the base of state (offsets are relative to this) */ 0, /* subtype this table refers to */ ARRAY_LENGTH(state_array), /* number of entries */ state_array /* array of entries */ }; #include "i86time.c" /***************************************************************************/ /* cpu state */ /***************************************************************************/ static struct i80x86_timing timing; static UINT8 parity_table[256]; /* The interrupt number of a pending external interrupt pending NMI is 2. */ /* For INTR interrupts, the level is caught on the bus during an INTA cycle */ #define PREFIX(name) i8086##name #define PREFIX86(name) i8086##name #define I8086 #include "instr86.h" #include "ea.h" #include "modrm.h" #include "table86.h" #include "instr86.c" #include "i86mem.c" #undef I8086 /***************************************************************************/ static void i8086_state_register(const device_config *device) { i8086_state *cpustate = get_safe_token(device); state_save_register_device_item_array(device, 0, cpustate->regs.w); state_save_register_device_item(device, 0, cpustate->pc); state_save_register_device_item(device, 0, cpustate->prevpc); state_save_register_device_item_array(device, 0, cpustate->base); state_save_register_device_item_array(device, 0, cpustate->sregs); state_save_register_device_item(device, 0, cpustate->flags); state_save_register_device_item(device, 0, cpustate->AuxVal); state_save_register_device_item(device, 0, cpustate->OverVal); state_save_register_device_item(device, 0, cpustate->SignVal); state_save_register_device_item(device, 0, cpustate->ZeroVal); state_save_register_device_item(device, 0, cpustate->CarryVal); state_save_register_device_item(device, 0, cpustate->DirVal); state_save_register_device_item(device, 0, cpustate->ParityVal); state_save_register_device_item(device, 0, cpustate->TF); state_save_register_device_item(device, 0, cpustate->IF); state_save_register_device_item(device, 0, cpustate->MF); state_save_register_device_item(device, 0, cpustate->int_vector); state_save_register_device_item(device, 0, cpustate->nmi_state); state_save_register_device_item(device, 0, cpustate->irq_state); state_save_register_device_item(device, 0, cpustate->extra_cycles); state_save_register_device_item(device, 0, cpustate->test_state); /* PJB 03/05 */ state_save_register_device_item(device, 0, cpustate->rep_in_progress); /* PJB 03/05 */ } static CPU_INIT( i8086 ) { i8086_state *cpustate = get_safe_token(device); unsigned int i, j, c; static const BREGS reg_name[8] = {AL, CL, DL, BL, AH, CH, DH, BH}; for (i = 0; i < 256; i++) { for (j = i, c = 0; j > 0; j >>= 1) if (j & 1) c++; parity_table[i] = !(c & 1); } for (i = 0; i < 256; i++) { Mod_RM.reg.b[i] = reg_name[(i & 0x38) >> 3]; Mod_RM.reg.w[i] = (WREGS) ((i & 0x38) >> 3); } for (i = 0xc0; i < 0x100; i++) { Mod_RM.RM.w[i] = (WREGS) (i & 7); Mod_RM.RM.b[i] = (BREGS) reg_name[i & 7]; } cpustate->irq_callback = irqcallback; cpustate->device = device; cpustate->program = memory_find_address_space(device, ADDRESS_SPACE_PROGRAM); cpustate->io = memory_find_address_space(device, ADDRESS_SPACE_IO); /* set up the state table */ cpustate->state = state_table_template; cpustate->state.baseptr = cpustate; cpustate->state.subtypemask = 1; i8086_state_register(device); configure_memory_16bit(cpustate); } static CPU_INIT( i8088 ) { i8086_state *cpustate = get_safe_token(device); CPU_INIT_CALL(i8086); configure_memory_8bit(cpustate); } static CPU_RESET( i8086 ) { i8086_state *cpustate = get_safe_token(device); cpu_irq_callback save_irqcallback; memory_interface save_mem; cpu_state_table save_state; save_irqcallback = cpustate->irq_callback; save_mem = cpustate->mem; save_state = cpustate->state; memset(cpustate, 0, sizeof(*cpustate)); cpustate->irq_callback = save_irqcallback; cpustate->mem = save_mem; cpustate->state = save_state; cpustate->device = device; cpustate->program = memory_find_address_space(device, ADDRESS_SPACE_PROGRAM); cpustate->io = memory_find_address_space(device, ADDRESS_SPACE_IO); cpustate->sregs[CS] = 0xf000; cpustate->base[CS] = SegBase(CS); cpustate->pc = 0xffff0 & AMASK; ExpandFlags(cpustate->flags); } static CPU_EXIT( i8086 ) { /* nothing to do ? */ } /* ASG 971222 -- added these interface functions */ static void set_irq_line(i8086_state *cpustate, int irqline, int state) { if (irqline == INPUT_LINE_NMI) { if (cpustate->nmi_state == state) return; cpustate->nmi_state = state; /* on a rising edge, signal the NMI */ if (state != CLEAR_LINE) PREFIX(_interrupt)(cpustate, I8086_NMI_INT_VECTOR); } else { cpustate->irq_state = state; /* if the IF is set, signal an interrupt */ if (state != CLEAR_LINE && cpustate->IF) PREFIX(_interrupt)(cpustate, -1); } } /* PJB 03/05 */ static void set_test_line(i8086_state *cpustate, int state) { cpustate->test_state = !state; } static CPU_EXECUTE( i8086 ) { i8086_state *cpustate = get_safe_token(device); /* copy over the cycle counts if they're not correct */ if (timing.id != 8086) timing = i8086_cycles; /* adjust for any interrupts that came in */ cpustate->icount = cycles; cpustate->icount -= cpustate->extra_cycles; cpustate->extra_cycles = 0; /* run until we're out */ while (cpustate->icount > 0) { LOG(("[%04x:%04x]=%02x\tF:%04x\tAX=%04x\tBX=%04x\tCX=%04x\tDX=%04x %d%d%d%d%d%d%d%d%d\n", cpustate->sregs[CS], cpustate->pc - cpustate->base[CS], ReadByte(cpustate->pc), cpustate->flags, cpustate->regs.w[AX], cpustate->regs.w[BX], cpustate->regs.w[CX], cpustate->regs.w[DX], cpustate->AuxVal ? 1 : 0, cpustate->OverVal ? 1 : 0, cpustate->SignVal ? 1 : 0, cpustate->ZeroVal ? 1 : 0, cpustate->CarryVal ? 1 : 0, cpustate->ParityVal ? 1 : 0, cpustate->TF, cpustate->IF, cpustate->DirVal < 0 ? 1 : 0)); debugger_instruction_hook(device, cpustate->pc); cpustate->seg_prefix = FALSE; cpustate->prevpc = cpustate->pc; TABLE86; } /* adjust for any interrupts that came in */ cpustate->icount -= cpustate->extra_cycles; cpustate->extra_cycles = 0; return cycles - cpustate->icount; } static CPU_DISASSEMBLE( i8086 ) { return i386_dasm_one(buffer, pc, oprom, 16); } #include "i86.h" #undef PREFIX #define PREFIX(name) i80186##name #define PREFIX186(name) i80186##name #define I80186 #include "instr186.h" #include "table186.h" #include "instr86.c" #include "instr186.c" #undef I80186 static CPU_EXECUTE( i80186 ) { i8086_state *cpustate = get_safe_token(device); /* copy over the cycle counts if they're not correct */ if (timing.id != 80186) timing = i80186_cycles; /* adjust for any interrupts that came in */ cpustate->icount = cycles; cpustate->icount -= cpustate->extra_cycles; cpustate->extra_cycles = 0; /* run until we're out */ while (cpustate->icount > 0) { LOG(("[%04x:%04x]=%02x\tAX=%04x\tBX=%04x\tCX=%04x\tDX=%04x\n", cpustate->sregs[CS], cpustate->pc, ReadByte(cpustate->pc), cpustate->regs.w[AX], cpustate->regs.w[BX], cpustate->regs.w[CX], cpustate->regs.w[DX])); debugger_instruction_hook(device, cpustate->pc); cpustate->seg_prefix = FALSE; cpustate->prevpc = cpustate->pc; TABLE186; } /* adjust for any interrupts that came in */ cpustate->icount -= cpustate->extra_cycles; cpustate->extra_cycles = 0; return cycles - cpustate->icount; } /************************************************************************** * STATE IMPORT/EXPORT **************************************************************************/ static CPU_IMPORT_STATE( i8086 ) { i8086_state *cpustate = get_safe_token(device); switch (entry->index) { case I8086_GENPC: if (cpustate->pc - cpustate->base[CS] >= 0x10000) { cpustate->base[CS] = cpustate->pc & 0xffff0; cpustate->sregs[CS] = cpustate->base[CS] >> 4; } break; case I8086_IP: cpustate->pc = cpustate->base[CS] + cpustate->ip; break; case I8086_GENSP: if (cpustate->sp - cpustate->base[SS] >= 0x10000) { cpustate->base[SS] = cpustate->sp & 0xffff0; cpustate->sregs[SS] = cpustate->base[SS] >> 4; } cpustate->regs.w[SP] = cpustate->sp - cpustate->base[SS]; break; case I8086_FLAGS: ExpandFlags(cpustate->flags); break; case I8086_ES: cpustate->base[ES] = SegBase(ES); break; case I8086_CS: cpustate->base[CS] = SegBase(CS); break; case I8086_SS: cpustate->base[SS] = SegBase(SS); break; case I8086_DS: cpustate->base[DS] = SegBase(DS); break; default: fatalerror("CPU_IMPORT_STATE(i8086) called for unexpected value\n"); break; } } static CPU_EXPORT_STATE( i8086 ) { i8086_state *cpustate = get_safe_token(device); switch (entry->index) { case I8086_IP: cpustate->ip = cpustate->pc - cpustate->base[CS]; break; case I8086_FLAGS: cpustate->flags = CompressFlags(); break; case I8086_GENSP: cpustate->sp = cpustate->base[SS] + cpustate->regs.w[SP]; break; default: fatalerror("CPU_EXPORT_STATE(i8086) called for unexpected value\n"); break; } } static CPU_EXPORT_STRING( i8086 ) { i8086_state *cpustate = get_safe_token(device); switch (entry->index) { case I8086_GENPC: sprintf(string, "%04X:%04X", cpustate->sregs[CS] & 0xffff, (cpustate->pc - cpustate->base[CS]) & 0xffff); break; case I8086_GENSP: sprintf(string, "%04X:%04X", cpustate->sregs[SS] & 0xffff, cpustate->regs.w[SP] & 0xffff); break; default: fatalerror("CPU_EXPORT_STRING(i8086) called for unexpected value\n"); break; } } /************************************************************************** * Generic set_info **************************************************************************/ static CPU_SET_INFO( i8086 ) { i8086_state *cpustate = get_safe_token(device); switch (state) { /* --- the following bits of info are set as 64-bit signed integers --- */ case CPUINFO_INT_INPUT_STATE + 0: set_irq_line(cpustate, 0, info->i); break; case CPUINFO_INT_INPUT_STATE + INPUT_LINE_NMI: set_irq_line(cpustate, INPUT_LINE_NMI, info->i); break; case CPUINFO_INT_INPUT_STATE + INPUT_LINE_TEST: set_test_line(cpustate, info->i); break; /* PJB 03/05 */ } } /************************************************************************** * Generic get_info **************************************************************************/ CPU_GET_INFO( i8086 ) { i8086_state *cpustate = (device != NULL && device->token != NULL) ? get_safe_token(device) : NULL; switch (state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case CPUINFO_INT_CONTEXT_SIZE: info->i = sizeof(i8086_state); break; case CPUINFO_INT_INPUT_LINES: info->i = 1; break; case CPUINFO_INT_DEFAULT_IRQ_VECTOR: info->i = 0xff; break; case DEVINFO_INT_ENDIANNESS: info->i = ENDIANNESS_LITTLE; break; case CPUINFO_INT_CLOCK_MULTIPLIER: info->i = 1; break; case CPUINFO_INT_CLOCK_DIVIDER: info->i = 1; break; case CPUINFO_INT_MIN_INSTRUCTION_BYTES: info->i = 1; break; case CPUINFO_INT_MAX_INSTRUCTION_BYTES: info->i = 15; break; case CPUINFO_INT_MIN_CYCLES: info->i = 1; break; case CPUINFO_INT_MAX_CYCLES: info->i = 50; break; case CPUINFO_INT_DATABUS_WIDTH_PROGRAM: info->i = 16; break; case CPUINFO_INT_ADDRBUS_WIDTH_PROGRAM: info->i = 20; break; case CPUINFO_INT_ADDRBUS_SHIFT_PROGRAM: info->i = 0; break; case CPUINFO_INT_DATABUS_WIDTH_DATA: info->i = 0; break; case CPUINFO_INT_ADDRBUS_WIDTH_DATA: info->i = 0; break; case CPUINFO_INT_ADDRBUS_SHIFT_DATA: info->i = 0; break; case CPUINFO_INT_DATABUS_WIDTH_IO: info->i = 16; break; case CPUINFO_INT_ADDRBUS_WIDTH_IO: info->i = 16; break; case CPUINFO_INT_ADDRBUS_SHIFT_IO: info->i = 0; break; case CPUINFO_INT_INPUT_STATE + 0: info->i = cpustate->irq_state; break; case CPUINFO_INT_INPUT_STATE + INPUT_LINE_NMI: info->i = cpustate->nmi_state; break; case CPUINFO_INT_INPUT_STATE + INPUT_LINE_TEST: info->i = cpustate->test_state; break; /* PJB 03/05 */ case CPUINFO_INT_PREVIOUSPC: info->i = cpustate->prevpc; break; /* --- the following bits of info are returned as pointers to data or functions --- */ case CPUINFO_FCT_SET_INFO: info->setinfo = CPU_SET_INFO_NAME(i8086); break; case CPUINFO_FCT_INIT: info->init = CPU_INIT_NAME(i8086); break; case CPUINFO_FCT_RESET: info->reset = CPU_RESET_NAME(i8086); break; case CPUINFO_FCT_EXIT: info->exit = CPU_EXIT_NAME(i8086); break; case CPUINFO_FCT_EXECUTE: info->execute = CPU_EXECUTE_NAME(i8086); break; case CPUINFO_FCT_BURN: info->burn = NULL; break; case CPUINFO_FCT_DISASSEMBLE: info->disassemble = CPU_DISASSEMBLE_NAME(i8086); break; case CPUINFO_PTR_INSTRUCTION_COUNTER: info->icount = &cpustate->icount; break; case CPUINFO_PTR_STATE_TABLE: info->state_table = &cpustate->state; break; case CPUINFO_FCT_IMPORT_STATE: info->import_state = CPU_IMPORT_STATE_NAME(i8086); break; case CPUINFO_FCT_EXPORT_STATE: info->export_state = CPU_EXPORT_STATE_NAME(i8086); break; case CPUINFO_FCT_EXPORT_STRING: info->export_string = CPU_EXPORT_STRING_NAME(i8086);break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case DEVINFO_STR_NAME: strcpy(info->s, "8086"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Intel 80x86"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.4"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Real mode i286 emulator v1.4 by Fabrice Frances\n(initial work cpustate->based on David Hedley's pcemu)"); break; case CPUINFO_STR_FLAGS: cpustate->flags = CompressFlags(); sprintf(info->s, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", cpustate->flags & 0x8000 ? '?' : '.', cpustate->flags & 0x4000 ? '?' : '.', cpustate->flags & 0x2000 ? '?' : '.', cpustate->flags & 0x1000 ? '?' : '.', cpustate->flags & 0x0800 ? 'O' : '.', cpustate->flags & 0x0400 ? 'D' : '.', cpustate->flags & 0x0200 ? 'I' : '.', cpustate->flags & 0x0100 ? 'T' : '.', cpustate->flags & 0x0080 ? 'S' : '.', cpustate->flags & 0x0040 ? 'Z' : '.', cpustate->flags & 0x0020 ? '?' : '.', cpustate->flags & 0x0010 ? 'A' : '.', cpustate->flags & 0x0008 ? '?' : '.', cpustate->flags & 0x0004 ? 'P' : '.', cpustate->flags & 0x0002 ? 'N' : '.', cpustate->flags & 0x0001 ? 'C' : '.'); break; } } /************************************************************************** * CPU-specific get_info/set_info **************************************************************************/ CPU_GET_INFO( i8088 ) { switch (state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case CPUINFO_INT_DATABUS_WIDTH_PROGRAM: info->i = 8; break; case CPUINFO_INT_DATABUS_WIDTH_IO: info->i = 8; break; /* --- the following bits of info are returned as pointers to data or functions --- */ case CPUINFO_FCT_INIT: info->init = CPU_INIT_NAME(i8088); break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case DEVINFO_STR_NAME: strcpy(info->s, "8088"); break; default: CPU_GET_INFO_CALL(i8086); break; } } /************************************************************************** * CPU-specific get_info/set_info **************************************************************************/ CPU_GET_INFO( i80186 ) { switch (state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case CPUINFO_INT_CLOCK_MULTIPLIER: info->i = 1; break; case CPUINFO_INT_CLOCK_DIVIDER: info->i = 2; break; /* --- the following bits of info are returned as pointers to data or functions --- */ case CPUINFO_FCT_EXECUTE: info->execute = CPU_EXECUTE_NAME(i80186);break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case DEVINFO_STR_NAME: strcpy(info->s, "80186"); break; default: CPU_GET_INFO_CALL(i8086); break; } } /************************************************************************** * CPU-specific get_info/set_info **************************************************************************/ CPU_GET_INFO( i80188 ) { switch (state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case CPUINFO_INT_DATABUS_WIDTH_PROGRAM: info->i = 8; break; case CPUINFO_INT_DATABUS_WIDTH_IO: info->i = 8; break; /* --- the following bits of info are returned as pointers to data or functions --- */ case CPUINFO_FCT_INIT: info->init = CPU_INIT_NAME(i8088); break; case CPUINFO_FCT_EXECUTE: info->execute = CPU_EXECUTE_NAME(i80186);break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case DEVINFO_STR_NAME: strcpy(info->s, "80188"); break; default: CPU_GET_INFO_CALL(i8086); break; } }