// license:BSD-3-Clause // copyright-holders:Carl // Peripheral code from rmnimbus driver by Phill Harvey-Smith which is // based on the Leland sound driver by Aaron Giles and Paul Leaman #include "emu.h" #include "i186.h" #include "debugger.h" #include "i86inline.h" #define LOG_PORTS 0 #define LOG_INTERRUPTS 0 #define LOG_INTERRUPTS_EXT 0 #define LOG_TIMER 0 #define LOG_DMA 0 /* external int priority masks */ #define EXTINT_CTRL_PRI_MASK 0x07 #define EXTINT_CTRL_MSK 0x08 #define EXTINT_CTRL_LTM 0x10 #define EXTINT_CTRL_CASCADE 0x20 #define EXTINT_CTRL_SFNM 0x40 /* DMA control register */ #define DEST_MIO 0x8000 #define DEST_DECREMENT 0x4000 #define DEST_INCREMENT 0x2000 #define DEST_NO_CHANGE (DEST_DECREMENT | DEST_INCREMENT) #define DEST_INCDEC_MASK (DEST_DECREMENT | DEST_INCREMENT) #define SRC_MIO 0x1000 #define SRC_DECREMENT 0x0800 #define SRC_INCREMENT 0x0400 #define SRC_NO_CHANGE (SRC_DECREMENT | SRC_INCREMENT) #define SRC_INCDEC_MASK (SRC_DECREMENT | SRC_INCREMENT) #define TERMINATE_ON_ZERO 0x0200 #define INTERRUPT_ON_ZERO 0x0100 #define SYNC_MASK 0x00C0 #define SYNC_SOURCE 0x0040 #define SYNC_DEST 0x0080 #define CHANNEL_PRIORITY 0x0020 #define TIMER_DRQ 0x0010 #define CHG_NOCHG 0x0004 #define ST_STOP 0x0002 #define BYTE_WORD 0x0001 /* these come from the Intel 80186 datasheet */ const uint8_t i80186_cpu_device::m_i80186_timing[] = { 45,28, /* exception, IRET */ 0, 2, 4, 3, /* INTs */ 2, /* segment overrides */ 2, 2, 3, /* flag operations */ 8, 7,19,15, /* arithmetic adjusts */ 4, 4, /* decimal adjusts */ 2, 4, /* sign extension */ 2,18, 6, 2, 6,11, /* misc */ 14,14,14, /* direct JMPs */ 11,17,26, /* indirect JMPs */ 15,23, /* direct CALLs */ 13,19,38, /* indirect CALLs */ 16,22,18,25, /* returns */ 4,13, 5,15, /* conditional JMPs */ 6,16, 6,16, /* loops */ 10,10, 8, 8, /* port reads */ 9, 9, 7, 7, /* port writes */ 2, 9,12, /* move, 8-bit */ 3,12, /* move, 8-bit immediate */ 2, 9,12, /* move, 16-bit */ 4,13, /* move, 16-bit immediate */ 8, 8, 9, 9, /* move, AL/AX memory */ 2,11, 2,11, /* move, segment registers */ 4,17, /* exchange, 8-bit */ 4,17, 3, /* exchange, 16-bit */ 10,16, 9, 9, /* pushes */ 10,20, 8, 8, /* pops */ 3,10,10, /* ALU ops, 8-bit */ 4,16,10, /* ALU ops, 8-bit immediate */ 3,10,10, /* ALU ops, 16-bit */ 4,16,10, /* ALU ops, 16-bit immediate */ 4,16,10, /* ALU ops, 16-bit w/8-bit immediate */ 26,35,32,41, /* MUL */ 25,34,31,40, /* IMUL */ 29,38,35,44, /* DIV */ 44,53,50,59, /* IDIV */ 3, 3,15,15, /* INC/DEC */ 3, 3,10,10, /* NEG/NOT */ 2, 5, 1, /* reg shift/rotate */ 15,17, 1, /* m8 shift/rotate */ 15,17, 1, /* m16 shift/rotate */ 22, 5,22, /* CMPS 8-bit */ 22, 5,22, /* CMPS 16-bit */ 15, 5,15, /* SCAS 8-bit */ 15, 5,15, /* SCAS 16-bit */ 12, 6,11, /* LODS 8-bit */ 12, 6,11, /* LODS 16-bit */ 10, 6, 9, /* STOS 8-bit */ 10, 6, 9, /* STOS 16-bit */ 14, 8, 8, /* MOVS 8-bit */ 14, 8, 8, /* MOVS 16-bit */ 14, 8, 8, /* (80186) INS 8-bit */ 14, 8, 8, /* (80186) INS 16-bit */ 14, 8, 8, /* (80186) OUTS 8-bit */ 14, 8, 8, /* (80186) OUTS 16-bit */ 14,68,83, /* (80186) PUSH immediate, PUSHA/POPA */ 22,29, /* (80186) IMUL immediate 8-bit */ 25,32, /* (80186) IMUL immediate 16-bit */ 15,25,4,16, 8, /* (80186) ENTER/LEAVE */ 33, /* (80186) BOUND */ }; DEFINE_DEVICE_TYPE(I80186, i80186_cpu_device, "i80186", "Intel 80186") DEFINE_DEVICE_TYPE(I80188, i80188_cpu_device, "i80188", "Intel 80188") i80188_cpu_device::i80188_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : i80186_cpu_device(mconfig, I80188, tag, owner, clock, 8) { memcpy(m_timing, m_i80186_timing, sizeof(m_i80186_timing)); set_irq_acknowledge_callback(*this, FUNC(i80186_cpu_device::inta_callback)); } i80186_cpu_device::i80186_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : i80186_cpu_device(mconfig, I80186, tag, owner, clock, 16) { memcpy(m_timing, m_i80186_timing, sizeof(m_i80186_timing)); set_irq_acknowledge_callback(*this, FUNC(i80186_cpu_device::inta_callback)); } i80186_cpu_device::i80186_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int data_bus_size) : i8086_common_cpu_device(mconfig, type, tag, owner, clock) , m_program_config("program", ENDIANNESS_LITTLE, data_bus_size, 20, 0) , m_opcodes_config("opcodes", ENDIANNESS_LITTLE, data_bus_size, 20, 0) , m_io_config("io", ENDIANNESS_LITTLE, data_bus_size, 16, 0) , m_read_slave_ack_func(*this) , m_out_chip_select_func(*this) , m_out_tmrout0_func(*this) , m_out_tmrout1_func(*this) , m_irmx_irq_cb(*this) , m_irmx_irq_ack(*this) { } device_memory_interface::space_config_vector i80186_cpu_device::memory_space_config() const { if(has_configured_map(AS_OPCODES)) return space_config_vector { std::make_pair(AS_PROGRAM, &m_program_config), std::make_pair(AS_OPCODES, &m_opcodes_config), std::make_pair(AS_IO, &m_io_config) }; else return space_config_vector { std::make_pair(AS_PROGRAM, &m_program_config), std::make_pair(AS_IO, &m_io_config) }; } uint8_t i80186_cpu_device::fetch() { uint8_t data = m_or8(update_pc()); m_ip++; return data; } void i80186_cpu_device::execute_run() { while(m_icount > 0 ) { if((m_dma[0].drq_state && (m_dma[0].control & ST_STOP)) || (m_dma[1].drq_state && (m_dma[1].control & ST_STOP))) { int channel = m_last_dma ? 0 : 1; m_last_dma = !m_last_dma; if(!(m_dma[1].drq_state && (m_dma[1].control & ST_STOP))) channel = 0; else if(!(m_dma[0].drq_state && (m_dma[0].control & ST_STOP))) channel = 1; else if((m_dma[0].control & CHANNEL_PRIORITY) && !(m_dma[1].control & CHANNEL_PRIORITY)) channel = 0; else if((m_dma[1].control & CHANNEL_PRIORITY) && !(m_dma[0].control & CHANNEL_PRIORITY)) channel = 1; m_icount--; drq_callback(channel); continue; } if ( m_seg_prefix_next ) { m_seg_prefix = true; m_seg_prefix_next = false; } else { m_prev_ip = m_ip; m_seg_prefix = false; /* Dispatch IRQ */ if ( m_pending_irq && m_no_interrupt == 0 ) { if ( m_pending_irq & NMI_IRQ ) { interrupt(2); m_pending_irq &= ~NMI_IRQ; m_halt = false; } else if ( m_IF ) { /* the actual vector is retrieved after pushing flags */ /* and clearing the IF */ interrupt(-1); m_halt = false; } } if(m_halt) { m_icount = 0; return; } /* No interrupt allowed between last instruction and this one */ if ( m_no_interrupt ) { m_no_interrupt--; } /* trap should allow one instruction to be executed */ if ( m_fire_trap ) { if ( m_fire_trap >= 2 ) { interrupt(1); m_fire_trap = 0; } else { m_fire_trap++; } } } debugger_instruction_hook( update_pc() ); uint8_t op = fetch_op(); switch(op) { case 0x60: // i_pusha { uint32_t tmp = m_regs.w[SP]; PUSH(m_regs.w[AX]); PUSH(m_regs.w[CX]); PUSH(m_regs.w[DX]); PUSH(m_regs.w[BX]); PUSH(tmp); PUSH(m_regs.w[BP]); PUSH(m_regs.w[SI]); PUSH(m_regs.w[DI]); CLK(PUSHA); } break; case 0x61: // i_popa m_regs.w[DI] = POP(); m_regs.w[SI] = POP(); m_regs.w[BP] = POP(); POP(); m_regs.w[BX] = POP(); m_regs.w[DX] = POP(); m_regs.w[CX] = POP(); m_regs.w[AX] = POP(); CLK(POPA); break; case 0x62: // i_bound { uint32_t low,high,tmp; m_modrm = fetch(); low = GetRMWord(); high = GetnextRMWord(); tmp = RegWord(); if (tmphigh) interrupt(5); CLK(BOUND); logerror("%06x: bound %04x high %04x low %04x tmp\n", m_pc, high, low, tmp); } break; case 0x68: // i_push_d16 PUSH( fetch_word() ); CLK(PUSH_IMM); break; case 0x69: // i_imul_d16 { uint32_t tmp; DEF_r16w(); tmp = fetch_word(); m_dst = (int32_t)((int16_t)m_src)*(int32_t)((int16_t)tmp); m_CarryVal = m_OverVal = (((int32_t)m_dst) >> 15 != 0) && (((int32_t)m_dst) >> 15 != -1); RegWord(m_dst); CLKM(IMUL_RRI16, IMUL_RMI16); } break; case 0x6a: // i_push_d8 PUSH( (uint16_t)((int16_t)((int8_t)fetch())) ); CLK(PUSH_IMM); break; case 0x6b: // i_imul_d8 { uint32_t src2; DEF_r16w(); src2= (uint16_t)((int16_t)((int8_t)fetch())); m_dst = (int32_t)((int16_t)m_src)*(int32_t)((int16_t)src2); m_CarryVal = m_OverVal = (((int32_t)m_dst) >> 15 != 0) && (((int32_t)m_dst) >> 15 != -1); RegWord(m_dst); CLKM(IMUL_RRI8, IMUL_RMI8); } break; case 0x6c: // i_insb i_insb(); break; case 0x6d: // i_insw i_insw(); break; case 0x6e: // i_outsb i_outsb(); break; case 0x6f: // i_outsw i_outsw(); break; case 0x8e: // i_mov_sregw m_modrm = fetch(); m_src = GetRMWord(); CLKM(MOV_SR,MOV_SM); switch (m_modrm & 0x38) { case 0x00: /* mov es,ew */ m_sregs[ES] = m_src; break; case 0x10: /* mov ss,ew */ m_sregs[SS] = m_src; m_no_interrupt = 1; break; case 0x18: /* mov ds,ew */ m_sregs[DS] = m_src; break; default: logerror("%06x: Mov Sreg - Invalid register\n", m_pc); m_ip = m_prev_ip; interrupt(6); break; } break; case 0xc0: // i_rotshft_bd8 { uint8_t c; m_modrm = fetch(); m_src = GetRMByte(); m_dst = m_src; c = fetch() & 0x1f; CLKM(ROT_REG_BASE,ROT_M8_BASE); m_icount -= m_timing[ROT_REG_BIT] * c; if (c) { switch ( m_modrm & 0x38 ) { case 0x00: do { ROL_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break; case 0x08: do { ROR_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break; case 0x10: do { ROLC_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break; case 0x18: do { RORC_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break; case 0x30: case 0x20: SHL_BYTE(c); break; case 0x28: SHR_BYTE(c); break; case 0x38: SHRA_BYTE(c); break; } } } break; case 0xc1: // i_rotshft_wd8 { uint8_t c; m_modrm = fetch(); m_src = GetRMWord(); m_dst = m_src; c = fetch() & 0x1f; CLKM(ROT_REG_BASE,ROT_M16_BASE); m_icount -= m_timing[ROT_REG_BIT] * c; if (c) { switch ( m_modrm & 0x38 ) { case 0x00: do { ROL_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break; case 0x08: do { ROR_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break; case 0x10: do { ROLC_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break; case 0x18: do { RORC_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break; case 0x30: case 0x20: SHL_WORD(c); break; case 0x28: SHR_WORD(c); break; case 0x38: SHRA_WORD(c); break; } } } break; case 0xc8: // i_enter { uint16_t nb = fetch(); uint32_t level; nb |= fetch() << 8; level = fetch(); CLK(!level ? ENTER0 : (level == 1) ? ENTER1 : ENTER_BASE); if(level > 1) m_icount -= level * m_timing[ENTER_COUNT]; PUSH(m_regs.w[BP]); m_regs.w[BP] = m_regs.w[SP]; m_regs.w[SP] -= nb; for (int i=1; i0); PutbackRMByte(m_dst); break; case 0x08: do { ROR_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break; case 0x10: do { ROLC_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break; case 0x18: do { RORC_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break; case 0x30: case 0x20: SHL_BYTE(c); break; case 0x28: SHR_BYTE(c); break; case 0x38: SHRA_BYTE(c); break; } } } break; case 0xd3: // i_rotshft_wcl { uint8_t c; m_modrm = fetch(); m_src = GetRMWord(); m_dst = m_src; c = m_regs.b[CL] & 0x1f; CLKM(ROT_REG_BASE,ROT_M16_BASE); m_icount -= m_timing[ROT_REG_BIT] * c; if (c) { switch ( m_modrm & 0x38 ) { case 0x00: do { ROL_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break; case 0x08: do { ROR_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break; case 0x10: do { ROLC_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break; case 0x18: do { RORC_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break; case 0x30: case 0x20: SHL_WORD(c); break; case 0x28: SHR_WORD(c); break; case 0x38: SHRA_WORD(c); break; } } } break; case 0xd8: // i_esc case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf: if(m_reloc & 0x8000) { m_ip = m_prev_ip; interrupt(7); break; } m_modrm = fetch(); GetRMByte(); CLK(NOP); // The 80187 has the FSTSW AX instruction if((m_modrm == 0xe0) && (op == 0xdf)) m_regs.w[AX] = 0xffff; // FPU not present break; case 0xe6: // i_outal write_port_byte_al(fetch()); CLK(OUT_IMM8); break; case 0xee: // i_outdxal write_port_byte_al(m_regs.w[DX]); CLK(OUT_DX8); break; case 0xf2: // i_repne case 0xf3: { bool pass = false; uint8_t next = repx_op(); uint16_t c = m_regs.w[CX]; switch (next) { case 0x6c: CLK(OVERRIDE); if (c) do { i_insb(); c--; } while (c>0 && m_icount>0); m_regs.w[CX]=c; m_seg_prefix = false; m_seg_prefix_next = false; break; case 0x6d: CLK(OVERRIDE); if (c) do { i_insw(); c--; } while (c>0 && m_icount>0); m_regs.w[CX]=c; m_seg_prefix = false; m_seg_prefix_next = false; break; case 0x6e: CLK(OVERRIDE); if (c) do { i_outsb(); c--; } while (c>0 && m_icount>0); m_regs.w[CX]=c; m_seg_prefix = false; m_seg_prefix_next = false; break; case 0x6f: CLK(OVERRIDE); if (c) do { i_outsw(); c--; } while (c>0 && m_icount>0); m_regs.w[CX]=c; m_seg_prefix = false; m_seg_prefix_next = false; break; default: // Decrement IP and pass on m_ip -= 1 + (m_seg_prefix_next ? 1 : 0); pass = true; break; } if(!pass) { if(c) m_ip = m_prev_ip; break; } } // through to default default: if(!common_op(op)) { m_icount -= 10; // UD fault timing? logerror("%06x: Invalid Opcode %02x\n", m_pc, op); m_ip = m_prev_ip; interrupt(6); // 80186 has #UD break; } } } } void i80186_cpu_device::device_start() { i8086_common_cpu_device::device_start(); state_add( I8086_ES, "ES", m_sregs[ES] ).formatstr("%04X"); state_add( I8086_CS, "CS", m_sregs[CS] ).callimport().formatstr("%04X"); state_add( I8086_SS, "SS", m_sregs[SS] ).formatstr("%04X"); state_add( I8086_DS, "DS", m_sregs[DS] ).formatstr("%04X"); state_add( I8086_VECTOR, "V", m_int_vector).formatstr("%02X"); state_add( I8086_PC, "PC", m_pc ).callimport().formatstr("%05X"); state_add( STATE_GENPCBASE, "CURPC", m_pc ).callimport().formatstr("%05X").noshow(); state_add( I8086_HALT, "HALT", m_halt ).mask(1); // Most of these mnemonics are borrowed from the Intel 80C186EA/80C188EA User's Manual. // (The 80C186EA/80C188EA's peripheral block is mapped incompatibly but mostly functionally analogous.) state_add( I80186_RELREG, "RELREG", m_reloc ).formatstr("%04X"); state_add( I80186_UMCS, "UMCS", m_mem.upper ).formatstr("%04X"); state_add( I80186_LMCS, "LMCS", m_mem.lower ).formatstr("%04X"); state_add( I80186_PACS, "PACS", m_mem.peripheral ).formatstr("%04X"); state_add( I80186_MMCS, "MMCS", m_mem.middle ).formatstr("%04X"); state_add( I80186_MPCS, "MPCS", m_mem.middle_size ).formatstr("%04X"); state_add( I80186_DxSRC + 0, "D0SRC", m_dma[0].source ).formatstr("%05X").mask(0xfffff); state_add( I80186_DxDST + 0, "D0DST", m_dma[0].dest ).formatstr("%05X").mask(0xfffff); state_add( I80186_DxTC + 0, "D0TC", m_dma[0].count ).formatstr("%04X"); state_add( I80186_DxCON + 0, "D0CON", m_dma[0].control ).formatstr("%04X"); state_add( I80186_DxSRC + 1, "D1SRC", m_dma[1].source ).formatstr("%05X").mask(0xfffff); state_add( I80186_DxDST + 1, "D1DST", m_dma[1].dest ).formatstr("%05X").mask(0xfffff); state_add( I80186_DxTC + 1, "D1TC", m_dma[1].count ).formatstr("%04X"); state_add( I80186_DxCON + 1, "D1CON", m_dma[1].control ).formatstr("%04X"); state_add( I80186_TxCNT + 0, "T0CNT", m_timer[0].count ).formatstr("%04X"); state_add( I80186_TxCMPA + 0, "T0CMPA", m_timer[0].maxA ).formatstr("%04X"); state_add( I80186_TxCMPB + 0, "T0CMPB", m_timer[0].maxB ).formatstr("%04X"); state_add( I80186_TxCON + 0, "T0CON", m_timer[0].control ).formatstr("%04X"); state_add( I80186_TxCNT + 1, "T1CNT", m_timer[1].count ).formatstr("%04X"); state_add( I80186_TxCMPA + 1, "T1CMPA", m_timer[1].maxA ).formatstr("%04X"); state_add( I80186_TxCMPB + 1, "T1CMPB", m_timer[1].maxB ).formatstr("%04X"); state_add( I80186_TxCON + 1, "T1CON", m_timer[1].control ).formatstr("%04X"); state_add( I80186_TxCNT + 2, "T2CNT", m_timer[2].count ).formatstr("%04X"); state_add( I80186_TxCMPA + 2, "T2CMPA", m_timer[2].maxA ).formatstr("%04X"); state_add( I80186_TxCON + 2, "T2CON", m_timer[2].control ).formatstr("%04X"); state_add( I80186_INSERV, "INSERV", m_intr.in_service ).formatstr("%04X"); state_add( I80186_REQST, "REQST", m_intr.request ).formatstr("%04X"); state_add( I80186_PRIMSK, "PRIMSK", m_intr.priority_mask ).formatstr("%04X"); state_add( I80186_INTSTS, "INTSTS", m_intr.status ).formatstr("%04X"); state_add( I80186_TCUCON, "TCUCON", m_intr.timer[0] ).formatstr("%04X"); state_add( I80186_DMA0CON, "DMA0CON", m_intr.dma[0] ).formatstr("%04X"); state_add( I80186_DMA1CON, "DMA1CON", m_intr.dma[1] ).formatstr("%04X"); state_add( I80186_I0CON, "I0CON", m_intr.ext[0] ).formatstr("%04X"); state_add( I80186_I1CON, "I1CON", m_intr.ext[1] ).formatstr("%04X"); state_add( I80186_I2CON, "I2CON", m_intr.ext[2] ).formatstr("%04X"); state_add( I80186_I3CON, "I3CON", m_intr.ext[3] ).formatstr("%04X"); state_add( I80186_POLLSTS, "POLLSTS", m_intr.poll_status ).formatstr("%04X"); // register for savestates save_item(NAME(m_timer[0].control)); save_item(NAME(m_timer[0].maxA)); save_item(NAME(m_timer[0].maxB)); save_item(NAME(m_timer[0].active_count)); save_item(NAME(m_timer[0].count)); save_item(NAME(m_timer[1].control)); save_item(NAME(m_timer[1].maxA)); save_item(NAME(m_timer[1].maxB)); save_item(NAME(m_timer[1].active_count)); save_item(NAME(m_timer[1].count)); save_item(NAME(m_timer[2].control)); save_item(NAME(m_timer[2].maxA)); save_item(NAME(m_timer[2].count)); save_item(NAME(m_dma[0].source)); save_item(NAME(m_dma[0].dest)); save_item(NAME(m_dma[0].count)); save_item(NAME(m_dma[0].control)); save_item(NAME(m_dma[1].source)); save_item(NAME(m_dma[1].dest)); save_item(NAME(m_dma[1].count)); save_item(NAME(m_dma[1].control)); save_item(NAME(m_intr.vector)); save_item(NAME(m_intr.pending)); save_item(NAME(m_intr.ack_mask)); save_item(NAME(m_intr.priority_mask)); save_item(NAME(m_intr.in_service)); save_item(NAME(m_intr.request)); save_item(NAME(m_intr.status)); save_item(NAME(m_intr.poll_status)); save_item(NAME(m_intr.timer)); save_item(NAME(m_intr.dma)); save_item(NAME(m_intr.ext)); save_item(NAME(m_intr.ext_state)); save_item(NAME(m_mem.lower)); save_item(NAME(m_mem.upper)); save_item(NAME(m_mem.middle)); save_item(NAME(m_mem.middle_size)); save_item(NAME(m_mem.peripheral)); save_item(NAME(m_reloc)); save_item(NAME(m_last_dma)); // zerofill memset(m_timer, 0, sizeof(m_timer)); memset(m_dma, 0, sizeof(m_dma)); memset(&m_intr, 0, sizeof(intr_state)); memset(&m_mem, 0, sizeof(mem_state)); m_reloc = 0; m_timer[0].int_timer = timer_alloc(TIMER_INT0); m_timer[1].int_timer = timer_alloc(TIMER_INT1); m_timer[2].int_timer = timer_alloc(TIMER_INT2); m_out_tmrout0_func.resolve_safe(); m_out_tmrout1_func.resolve_safe(); m_read_slave_ack_func.resolve_safe(0); m_out_chip_select_func.resolve_safe(); m_irmx_irq_cb.resolve_safe(); m_irmx_irq_ack.resolve(); } void i80186_cpu_device::device_reset() { i8086_common_cpu_device::device_reset(); /* reset the interrupt state */ m_intr.priority_mask = 0x0007; m_intr.timer[0] = 0x000f; m_intr.timer[1] = 0x000f; m_intr.timer[2] = 0x000f; m_intr.dma[0] = 0x000f; m_intr.dma[1] = 0x000f; m_intr.ext[0] = 0x000f; m_intr.ext[1] = 0x000f; m_intr.ext[2] = 0x000f; m_intr.ext[3] = 0x000f; m_intr.in_service = 0x0000; m_intr.pending = 0x0000; m_intr.ack_mask = 0x0000; m_intr.request = 0x0000; m_intr.status = 0x0000; m_intr.poll_status = 0x0000; m_intr.ext_state = 0x00; m_reloc = 0x20ff; m_mem.upper = 0xfffb; for (auto & elem : m_dma) { elem.drq_state = false; elem.control = 0; } for (auto & elem : m_timer) { elem.control = 0; elem.maxA = 0; elem.maxB = 0; elem.active_count = false; elem.count = 0; } } uint8_t i80186_cpu_device::read_port_byte(uint16_t port) { if(!(m_reloc & 0x1000) && (port >> 8) == (m_reloc & 0xff)) { if(port & 1) return internal_port_r(*m_io, (port >> 1) & 0x7f, 0xff00) >> 8; else return internal_port_r(*m_io, (port >> 1) & 0x7f, 0x00ff) & 0xff; } return m_io->read_byte(port); } uint16_t i80186_cpu_device::read_port_word(uint16_t port) { if(!(m_reloc & 0x1000) && (port >> 8) == (m_reloc & 0xff)) { // Unaligned reads from the internal bus are swapped rather than split if(port & 1) return swapendian_int16(internal_port_r(*m_io, (port >> 1) & 0x7f)); else return internal_port_r(*m_io, (port >> 1) & 0x7f); } return m_io->read_word_unaligned(port); } void i80186_cpu_device::write_port_byte(uint16_t port, uint8_t data) { if(!(m_reloc & 0x1000) && (port >> 8) == (m_reloc & 0xff)) { if(port & 1) internal_port_w(*m_io, (port >> 1) & 0x7f, data << 8, 0xff00); else internal_port_w(*m_io, (port >> 1) & 0x7f, data, 0x00ff); } else m_io->write_byte(port, data); } void i80186_cpu_device::write_port_byte_al(uint16_t port) { if(!(m_reloc & 0x1000) && (port >> 8) == (m_reloc & 0xff)) { // Both AH and AL are written onto the internal bus if(port & 1) internal_port_w(*m_io, (port >> 1) & 0x7f, swapendian_int16(m_regs.w[AX]), 0xff00); else internal_port_w(*m_io, (port >> 1) & 0x7f, m_regs.w[AX], 0x00ff); } else m_io->write_byte(port, m_regs.w[AL]); } void i80186_cpu_device::write_port_word(uint16_t port, uint16_t data) { if(!(m_reloc & 0x1000) && (port >> 8) == (m_reloc & 0xff)) { // Unaligned writes to the internal bus are swapped rather than split if(port & 1) internal_port_w(*m_io, (port >> 1) & 0x7f, swapendian_int16(data)); else internal_port_w(*m_io, (port >> 1) & 0x7f, data); } else m_io->write_word_unaligned(port, data); } uint8_t i80186_cpu_device::read_byte(uint32_t addr) { if((m_reloc & 0x1000) && (addr >> 8) == (m_reloc & 0xfff)) { uint16_t ret = internal_port_r(*m_program, (addr >> 1) & 0x7f, (addr & 1) ? 0xff00 : 0x00ff); return (addr & 1) ? (ret >> 8) : (ret & 0xff); } return m_program->read_byte(addr); } uint16_t i80186_cpu_device::read_word(uint32_t addr) { if((m_reloc & 0x1000) && (addr >> 8) == (m_reloc & 0xfff)) { // Unaligned reads from the internal bus are swapped rather than split if(addr & 1) return swapendian_int16(internal_port_r(*m_program, (addr >> 1) & 0x7f)); else return internal_port_r(*m_program, (addr >> 1) & 0x7f); } return m_program->read_word_unaligned(addr); } void i80186_cpu_device::write_byte(uint32_t addr, uint8_t data) { if((m_reloc & 0x1000) && (addr >> 8) == (m_reloc & 0xfff)) internal_port_w(*m_program, (addr >> 1) & 0x7f, (addr & 1) ? (data << 8) : data, (addr & 1) ? 0xff00 : 0x00ff); else m_program->write_byte(addr, data); } void i80186_cpu_device::write_word(uint32_t addr, uint16_t data) { if((m_reloc & 0x1000) && (addr >> 8) == (m_reloc & 0xfff)) { // Unaligned writes from the internal bus are swapped rather than split if(addr & 1) internal_port_w(*m_program, (addr >> 1) & 0x7f, swapendian_int16(data)); else internal_port_w(*m_program, (addr >> 1) & 0x7f, data); } else m_program->write_word_unaligned(addr, data); } /************************************* * * 80186 interrupt controller * *************************************/ IRQ_CALLBACK_MEMBER(i80186_cpu_device::inta_callback) { if (BIT(m_reloc, 14)) { if (!m_irmx_irq_ack.isnull()) return m_irmx_irq_ack(device, irqline); return 0; } return int_callback(device, irqline); } IRQ_CALLBACK_MEMBER(i80186_cpu_device::int_callback) { uint8_t vector; uint16_t old; uint16_t oldreq; if (LOG_INTERRUPTS) logerror("(%f) **** Acknowledged interrupt vector %02X\n", machine().time().as_double(), m_intr.poll_status & 0x1f); /* clear the interrupt */ set_input_line(0, CLEAR_LINE); m_intr.pending = 0; oldreq = m_intr.request; /* clear the request and set the in-service bit */ if(m_intr.ack_mask & 0xf0) { int i; for(i = 0; i < 4; i++) if((m_intr.ack_mask >> (i + 4)) & 1) break; if(!(m_intr.ext[i] & EXTINT_CTRL_LTM)) m_intr.request &= ~m_intr.ack_mask; } else m_intr.request &= ~m_intr.ack_mask; if((LOG_INTERRUPTS) && (m_intr.request!=oldreq)) logerror("intr.request changed from %02X to %02X\n",oldreq,m_intr.request); old = m_intr.in_service; m_intr.in_service |= m_intr.ack_mask; if((LOG_INTERRUPTS) && (m_intr.in_service!=old)) logerror("intr.in_service changed from %02X to %02X\n",old,m_intr.in_service); if (!BIT(m_reloc, 14)) { if (m_intr.ack_mask == 0x0001) { switch (m_intr.poll_status & 0x1f) { case 0x08: m_intr.status &= ~0x01; break; case 0x12: m_intr.status &= ~0x02; break; case 0x13: m_intr.status &= ~0x04; break; } } /* return the vector */ switch(m_intr.poll_status & 0x1F) { case 0x0C: vector = (m_intr.ext[0] & EXTINT_CTRL_CASCADE) ? m_read_slave_ack_func(0) : (m_intr.poll_status & 0x1f); break; case 0x0D: vector = (m_intr.ext[1] & EXTINT_CTRL_CASCADE) ? m_read_slave_ack_func(1) : (m_intr.poll_status & 0x1f); break; default: vector = m_intr.poll_status & 0x1f; break; } } else { if (m_intr.ack_mask & 0x31) { if (m_intr.ack_mask & 1) m_intr.status &= ~0x01; else if(m_intr.ack_mask & 0x10) m_intr.status &= ~0x02; else if(m_intr.ack_mask & 0x20) m_intr.status &= ~0x04; } vector = m_intr.poll_status & 0xff; } m_intr.ack_mask = 0; /* a request no longer pending */ m_intr.poll_status &= ~0x8000; if (LOG_INTERRUPTS) { logerror("intr.ext[0]=%04X intr.ext[1]=%04X\n",m_intr.ext[0],m_intr.ext[1]); logerror("Int %02X Calling vector %02X\n",m_intr.poll_status,vector); } return vector; } void i80186_cpu_device::update_interrupt_state() { int new_vector = 0; int Priority; int IntNo; if (LOG_INTERRUPTS) logerror("update_interrupt_status: req=%04X stat=%04X serv=%04X priority_mask=%4X\n", m_intr.request, m_intr.status, m_intr.in_service, m_intr.priority_mask); /* loop over priorities */ for (Priority = 0; Priority <= m_intr.priority_mask; Priority++) { /* note: by checking 4 bits, we also verify that the mask is off */ if (BIT(m_reloc, 14)) { for (IntNo = 0; IntNo < 3; IntNo++) { if ((m_intr.timer[IntNo] & 0x0F) == Priority) { int irq = (1 << IntNo); /* if we're already servicing something at this level, don't generate anything new */ if (m_intr.in_service & 0x01) return; /* if there's something pending, generate an interrupt */ if (m_intr.status & irq) { new_vector = m_intr.vector | Priority; /* set the clear mask and generate the int */ m_intr.ack_mask = IntNo ? (8 << IntNo) : 1; goto generate_int; } } } } else { if ((m_intr.timer[0] & 0x0F) == Priority) { /* if we're already servicing something at this level, don't generate anything new */ if (m_intr.in_service & 0x01) return; /* if there's something pending, generate an interrupt */ if (m_intr.status & 0x07) { if (m_intr.status & 1) new_vector = 0x08; else if (m_intr.status & 2) new_vector = 0x12; else if (m_intr.status & 4) new_vector = 0x13; else logerror("Invalid timer interrupt!\n"); /* set the clear mask and generate the int */ m_intr.ack_mask = 0x0001; goto generate_int; } } } /* check DMA interrupts */ for (IntNo = 0; IntNo < 2; IntNo++) if ((m_intr.dma[IntNo] & 0x0F) == Priority) { /* if we're already servicing something at this level, don't generate anything new */ if (m_intr.in_service & (0x04 << IntNo)) return; /* if there's something pending, generate an interrupt */ if (m_intr.request & (0x04 << IntNo)) { if (BIT(m_reloc, 14)) new_vector = m_intr.vector | Priority; else new_vector = 0x0a + IntNo; /* set the clear mask and generate the int */ m_intr.ack_mask = 0x0004 << IntNo; goto generate_int; } } if (BIT(m_reloc, 14)) continue; /* check external interrupts */ for (IntNo = 0; IntNo < 4; IntNo++) { if ((m_intr.ext[IntNo] & 0x0F) == Priority) { if (LOG_INTERRUPTS) logerror("Int%d priority=%d\n",IntNo,Priority); /* if we're already servicing something at this level, don't generate anything new */ if ((m_intr.in_service & (0x10 << IntNo)) && !(m_intr.ext[IntNo] & EXTINT_CTRL_SFNM)) return; /* if there's something pending, generate an interrupt */ if (m_intr.request & (0x10 << IntNo)) { if((IntNo >= 2) && (m_intr.ext[IntNo - 2] & EXTINT_CTRL_CASCADE)) { logerror("i186: %06x: irq %d use when set for cascade mode\n", m_pc, IntNo); m_intr.request &= ~(0x10 << IntNo); continue; } /* otherwise, generate an interrupt for this request */ new_vector = 0x0c + IntNo; /* set the clear mask and generate the int */ m_intr.ack_mask = 0x0010 << IntNo; goto generate_int; } else if ((m_intr.in_service & (0x10 << IntNo)) && (m_intr.ext[IntNo] & EXTINT_CTRL_SFNM)) return; // if an irq is in service and sfnm is enabled, stop here } } } m_intr.pending = 0; if (!BIT(m_reloc, 14)) set_input_line(0, CLEAR_LINE); else m_irmx_irq_cb(CLEAR_LINE); return; generate_int: /* generate the appropriate interrupt */ m_intr.poll_status = 0x8000 | new_vector; if (!m_intr.pending) { if (!BIT(m_reloc, 14)) set_input_line(0, ASSERT_LINE); else m_irmx_irq_cb(ASSERT_LINE); } m_intr.pending = 1; if (LOG_INTERRUPTS) logerror("(%f) **** Requesting interrupt vector %02X\n", machine().time().as_double(), new_vector); } void i80186_cpu_device::handle_eoi(int data) { int Priority; int IntNo; int handled=0; /* specific case */ if (!(data & 0x8000)) { /* turn off the appropriate in-service bit */ switch (data & 0x1f) { case 0x08: m_intr.in_service &= ~0x01; break; case 0x12: m_intr.in_service &= ~0x01; break; case 0x13: m_intr.in_service &= ~0x01; break; case 0x0a: m_intr.in_service &= ~0x04; break; case 0x0b: m_intr.in_service &= ~0x08; break; case 0x0c: m_intr.in_service &= ~0x10; break; case 0x0d: m_intr.in_service &= ~0x20; break; case 0x0e: m_intr.in_service &= ~0x40; break; case 0x0f: m_intr.in_service &= ~0x80; break; default: logerror("%05X:ERROR - 80186 EOI with unknown vector %02X\n", m_pc, data & 0x1f); } if (LOG_INTERRUPTS) logerror("(%f) **** Got EOI for vector %02X\n", machine().time().as_double(), data & 0x1f); } /* non-specific case */ else { /* loop over priorities */ for (Priority = 0; ((Priority <= 7) && !handled); Priority++) { /* check for in-service timers */ if (BIT(m_reloc, 14)) { for (IntNo = 0; ((IntNo < 2) && !handled) ; IntNo++) { int mask = IntNo ? (8 << IntNo) : 1; if ((m_intr.timer[IntNo] & 0x07) == Priority && (m_intr.in_service & mask)) { m_intr.in_service &= ~mask; if (LOG_INTERRUPTS) logerror("(%f) **** Got EOI for timer%d\n", machine().time().as_double(), IntNo); handled=1; } } } else { if ((m_intr.timer[0] & 0x07) == Priority && (m_intr.in_service & 0x01)) { m_intr.in_service &= ~0x01; if (LOG_INTERRUPTS) logerror("(%f) **** Got EOI for timer\n", machine().time().as_double()); handled=1; } } /* check for in-service DMA interrupts */ for (IntNo = 0; ((IntNo < 2) && !handled) ; IntNo++) if ((m_intr.dma[IntNo] & 0x07) == Priority && (m_intr.in_service & (0x04 << IntNo))) { m_intr.in_service &= ~(0x04 << IntNo); if (LOG_INTERRUPTS) logerror("(%f) **** Got EOI for DMA%d\n", machine().time().as_double(), IntNo); handled=1; } if (BIT(m_reloc, 14)) continue; /* check external interrupts */ for (IntNo = 0; ((IntNo < 4) && !handled) ; IntNo++) if ((m_intr.ext[IntNo] & 0x07) == Priority && (m_intr.in_service & (0x10 << IntNo))) { m_intr.in_service &= ~(0x10 << IntNo); if (LOG_INTERRUPTS) logerror("(%f) **** Got EOI for INT%d\n", machine().time().as_double(), IntNo); handled=1; } } } update_interrupt_state(); } /* Trigger an external interrupt, optionally supplying the vector to take */ void i80186_cpu_device::external_int(uint16_t intno, int state) { if (BIT(m_reloc, 14)) { if (!intno) { set_input_line(0, state); return; } logerror("irq to line %d in irmx mode\n",intno); return; } if (!(m_intr.ext_state & (1 << intno)) == !state) return; if (LOG_INTERRUPTS_EXT) logerror("generating external int %02X\n",intno); if (!state) { m_intr.request &= ~(0x10 << intno); m_intr.ack_mask &= ~(0x10 << intno); m_intr.ext_state &= ~(1 << intno); } else // Turn on the requested request bit and handle interrupt { m_intr.request |= (0x10 << intno); m_intr.ext_state |= (1 << intno); } update_interrupt_state(); } /************************************* * * 80186 internal timers * *************************************/ void i80186_cpu_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch(id) { case TIMER_INT0: case TIMER_INT1: case TIMER_INT2: { int which = param; timer_state *t = &m_timer[which]; if (LOG_TIMER) logerror("Hit interrupt callback for timer %d\n", which); /* set the max count bit */ t->control |= 0x0020; /* request an interrupt */ if (t->control & 0x2000) { m_intr.status |= 0x01 << which; update_interrupt_state(); if (LOG_TIMER) logerror(" Generating timer interrupt\n"); } if(which == 2) { if((m_dma[0].control & (TIMER_DRQ | ST_STOP)) == TIMER_DRQ) drq_callback(0); if((m_dma[1].control & (TIMER_DRQ | ST_STOP)) == TIMER_DRQ) drq_callback(1); if((m_timer[0].control & 0x800c) == 0x8008) inc_timer(0); if((m_timer[1].control & 0x800c) == 0x8008) inc_timer(1); } else { if(!(t->control & 2)) { if(which) m_out_tmrout1_func(1); else m_out_tmrout0_func(1); } else { if(which) m_out_tmrout1_func(t->active_count); else m_out_tmrout0_func(t->active_count); } } /* if we're continuous or altcounting, reset */ if((t->control & 1) || ((t->control & 2) && (which != 2) && !t->active_count)) { int count; if((t->control & 2) && (which != 2)) { count = t->active_count ? t->maxA : t->maxB; if(!t->active_count) { t->active_count = 1; t->control |= 0x1000; } else { t->active_count = 0; t->control &= ~0x1000; } } else count = t->maxA; count = count ? count : 0x10000; if(!(t->control & 4)) t->int_timer->adjust((attotime::from_hz(clock()/8) * count), which); if (LOG_TIMER) logerror(" Repriming interrupt\n"); } else { t->int_timer->adjust(attotime::never, which); t->control &= ~0x9000; } t->count = 0; break; } default: break; } } void i80186_cpu_device::internal_timer_sync(int which) { timer_state *t = &m_timer[which]; /* if we have a timing timer running, adjust the count */ if ((t->control & 0x8000) && !(t->control & 0x0c)) t->count = (((which != 2) && t->active_count) ? t->maxB : t->maxA) - t->int_timer->remaining().as_ticks(clock() / 8); } void i80186_cpu_device::inc_timer(int which) { timer_state *t = &m_timer[which]; t->count++; if (t->control & 2) { if (t->count == (t->active_count ? t->maxB : t->maxA)) device_timer(*t->int_timer, which, which, nullptr); } else if (t->count == t->maxA) device_timer(*t->int_timer, which, which, nullptr); } void i80186_cpu_device::internal_timer_update(int which, int new_count, int new_maxA, int new_maxB, int new_control) { timer_state *t = &m_timer[which]; int update_int_timer = 0; if (LOG_TIMER) logerror("internal_timer_update: %d, new_count=%d, new_maxA=%d, new_maxB=%d,new_control=%d\n", which, new_count, new_maxA, new_maxB, new_control); /* if we have a new count and we're on, update things */ if (new_count != -1) { if (t->control & 0x8000) { internal_timer_sync(which); update_int_timer = 1; } t->count = new_count; } /* if we have a new max and we're on, update things */ if (new_maxA != -1 && new_maxA != t->maxA) { if (t->control & 0x8000) { internal_timer_sync(which); update_int_timer = 1; } t->maxA = new_maxA; if (new_maxA == 0) { new_maxA = 0x10000; } } /* if we have a new max and we're on, update things */ if (new_maxB != -1 && new_maxB != t->maxB) { if (t->control & 0x8000) { internal_timer_sync(which); update_int_timer = 1; } t->maxB = new_maxB; if (new_maxB == 0) { new_maxB = 0x10000; } } /* handle control changes */ if (new_control != -1) { int diff; uint16_t resbits = (which == 2) ? 0x1fde : 0x1fc0; /* merge back in the bits we don't modify */ new_control = (new_control & ~resbits) | (t->control & resbits); /* handle the /INH bit */ if (!(new_control & 0x4000)) new_control = (new_control & ~0x8000) | (t->control & 0x8000); new_control &= ~0x4000; /* check for control bits we don't handle */ diff = new_control ^ t->control; if (diff & 0x0010) logerror("%05X:ERROR! -unsupported timer mode %04X\n", m_pc, new_control); /* if we have real changes, update things */ if (diff != 0) { /* if we're going off, make sure our timers are gone */ if ((diff & 0x8000) && !(new_control & 0x8000)) { /* compute the final count */ internal_timer_sync(which); update_int_timer = 1; } /* if we're going on, start the timers running except with external clock or prescale */ else if ((diff & 0x8000) && (new_control & 0x8000) && !(new_control & 0xc)) { update_int_timer = 1; } /* if something about the interrupt timer changed, force an update */ if (!(diff & 0x8000) && (diff & 0x2000)) { internal_timer_sync(which); update_int_timer = 1; } } /* set the new control register */ t->control = new_control; } /* update the interrupt timer */ if (update_int_timer) { t->active_count = 0; t->control &= ~0x1000; if ((t->control & 0x8000) && !(t->control & 4)) { int diff = t->maxA - t->count; if (diff <= 0) diff += 0x10000; t->int_timer->adjust(attotime::from_hz(clock()/8) * diff, which); if (LOG_TIMER) logerror("Set interrupt timer for %d\n", which); } else { t->int_timer->adjust(attotime::never, which); } } } /************************************* * * 80186 internal DMA * *************************************/ void i80186_cpu_device::update_dma_control(int which, int new_control) { dma_state *d = &m_dma[which]; int diff; /* handle the CHG bit */ if (!(new_control & CHG_NOCHG)) new_control = (new_control & ~ST_STOP) | (d->control & ST_STOP); new_control &= ~CHG_NOCHG; /* check for control bits we don't handle */ diff = new_control ^ d->control; if ((LOG_DMA) && (diff & 0x6811)) logerror("%05X:ERROR! - unsupported DMA mode %04X\n", m_pc, new_control); if (LOG_DMA) logerror("Initiated DMA %d - count = %04X, source = %04X, dest = %04X\n", which, d->count, d->source, d->dest); /* set the new control register */ d->control = new_control; } void i80186_cpu_device::drq_callback(int which) { dma_state *dma = &m_dma[which]; uint16_t dma_word; uint8_t dma_byte; uint8_t incdec_size; if (LOG_DMA>1) logerror("Control=%04X, src=%05X, dest=%05X, count=%04X\n",dma->control,dma->source,dma->dest,dma->count); if (!(dma->control & ST_STOP)) { if(LOG_DMA) logerror("%05X:ERROR! - drq%d with dma channel stopped\n", m_pc, which); return; } address_space *dest_space = (dma->control & DEST_MIO) ? m_program : m_io; address_space *src_space = (dma->control & SRC_MIO) ? m_program : m_io; // Do the transfer, 80188 is incapable of word transfers if ((dma->control & BYTE_WORD) && (m_program->data_width() == 16)) { dma_word = src_space->read_word_unaligned(dma->source); dest_space->write_word_unaligned(dma->dest, dma_word); incdec_size = 2; } else { dma_byte = src_space->read_byte(dma->source); dest_space->write_byte(dma->dest, dma_byte); incdec_size = 1; } // Increment or Decrement destination and source pointers as needed switch (dma->control & DEST_INCDEC_MASK) { case DEST_DECREMENT: dma->dest -= incdec_size; break; case DEST_INCREMENT: dma->dest += incdec_size; break; } switch (dma->control & SRC_INCDEC_MASK) { case SRC_DECREMENT: dma->source -= incdec_size; break; case SRC_INCREMENT: dma->source += incdec_size; break; } // decrement count dma->count -= 1; // Terminate if count is zero, and terminate flag set if (((dma->control & TERMINATE_ON_ZERO) || !(dma->control & SYNC_MASK)) && (dma->count == 0)) { dma->control &= ~ST_STOP; if (LOG_DMA) logerror("DMA terminated\n"); } // Interrupt if count is zero, and interrupt flag set if ((dma->control & INTERRUPT_ON_ZERO) && (dma->count == 0)) { if (LOG_DMA>1) logerror("DMA%d - requesting interrupt: count = %04X, source = %04X\n", which, dma->count, dma->source); m_intr.request |= 0x04 << which; update_interrupt_state(); } } READ16_MEMBER(i80186_cpu_device::internal_port_r) { int temp, which; switch (offset) { case 0x10: if (LOG_PORTS) logerror("%05X:read interrupt vector\n", m_pc); return m_intr.vector; case 0x11: if (LOG_PORTS) logerror("%05X:ERROR - read from 80186 EOI\n", m_pc); break; case 0x12: if (LOG_PORTS) logerror("%05X:read 80186 interrupt poll\n", m_pc); if (m_intr.poll_status & 0x8000) inta_callback(*this, 0); return m_intr.poll_status; case 0x13: if (LOG_PORTS) logerror("%05X:read 80186 interrupt poll status\n", m_pc); return m_intr.poll_status; case 0x14: if (LOG_PORTS) logerror("%05X:read 80186 interrupt mask\n", m_pc); temp = (m_intr.timer[0] >> 3) & 0x01; temp |= (m_intr.dma[0] >> 1) & 0x04; temp |= (m_intr.dma[1] >> 0) & 0x08; if (BIT(m_reloc, 14)) { temp |= (m_intr.timer[1] << 1) & 0x10; temp |= (m_intr.timer[2] << 2) & 0x20; } else { temp |= (m_intr.ext[0] << 1) & 0x10; temp |= (m_intr.ext[1] << 2) & 0x20; temp |= (m_intr.ext[2] << 3) & 0x40; temp |= (m_intr.ext[3] << 4) & 0x80; } return temp; case 0x15: if (LOG_PORTS) logerror("%05X:read 80186 interrupt priority mask\n", m_pc); return m_intr.priority_mask; case 0x16: if (LOG_PORTS) logerror("%05X:read 80186 interrupt in-service\n", m_pc); return m_intr.in_service; case 0x17: if (LOG_PORTS) logerror("%05X:read 80186 interrupt request\n", m_pc); temp = m_intr.request & ~0x0001; if (m_intr.status & 0x0007) temp |= 1; return temp; case 0x18: if (LOG_PORTS) logerror("%05X:read 80186 interrupt status\n", m_pc); return m_intr.status; case 0x19: if (LOG_PORTS) logerror("%05X:read 80186 timer interrupt control\n", m_pc); return m_intr.timer[0]; case 0x1a: if (LOG_PORTS) logerror("%05X:read 80186 DMA 0 interrupt control\n", m_pc); return m_intr.dma[0]; case 0x1b: if (LOG_PORTS) logerror("%05X:read 80186 DMA 1 interrupt control\n", m_pc); return m_intr.dma[1]; case 0x1c: if (LOG_PORTS) logerror("%05X:read 80186 INT 0 interrupt control\n", m_pc); if (BIT(m_reloc, 14)) return m_intr.timer[1]; else return m_intr.ext[0]; case 0x1d: if (LOG_PORTS) logerror("%05X:read 80186 INT 1 interrupt control\n", m_pc); if (BIT(m_reloc, 14)) return m_intr.timer[2]; else return m_intr.ext[1]; case 0x1e: if (LOG_PORTS) logerror("%05X:read 80186 INT 2 interrupt control\n", m_pc); if (BIT(m_reloc, 14)) return 0; else return m_intr.ext[2]; case 0x1f: if (LOG_PORTS) logerror("%05X:read 80186 INT 3 interrupt control\n", m_pc); if (BIT(m_reloc, 14)) return 0; else return m_intr.ext[3]; case 0x28: case 0x2c: case 0x30: if (LOG_PORTS) logerror("%05X:read 80186 Timer %d count\n", m_pc, (offset - 0x28) / 4); which = (offset - 0x28) / 4; if (ACCESSING_BITS_0_7) internal_timer_sync(which); return m_timer[which].count; case 0x29: case 0x2d: case 0x31: if (LOG_PORTS) logerror("%05X:read 80186 Timer %d max A\n", m_pc, (offset - 0x29) / 4); which = (offset - 0x29) / 4; return m_timer[which].maxA; case 0x2a: case 0x2e: if (LOG_PORTS) logerror("%05X:read 80186 Timer %d max B\n", m_pc, (offset - 0x2a) / 4); which = (offset - 0x2a) / 4; return m_timer[which].maxB; case 0x2b: case 0x2f: case 0x33: if (LOG_PORTS) logerror("%05X:read 80186 Timer %d control\n", m_pc, (offset - 0x2b) / 4); which = (offset - 0x2b) / 4; return m_timer[which].control; case 0x50: if (LOG_PORTS) logerror("%05X:read 80186 upper chip select\n", m_pc); return m_mem.upper; case 0x51: if (LOG_PORTS) logerror("%05X:read 80186 lower chip select\n", m_pc); return m_mem.lower; case 0x52: if (LOG_PORTS) logerror("%05X:read 80186 peripheral chip select\n", m_pc); return m_mem.peripheral; case 0x53: if (LOG_PORTS) logerror("%05X:read 80186 middle chip select\n", m_pc); return m_mem.middle; case 0x54: if (LOG_PORTS) logerror("%05X:read 80186 middle P chip select\n", m_pc); return m_mem.middle_size; case 0x60: case 0x68: if (LOG_PORTS) logerror("%05X:read 80186 DMA%d lower source address\n", m_pc, (offset - 0x60) / 8); which = (offset - 0x60) / 8; return m_dma[which].source; case 0x61: case 0x69: if (LOG_PORTS) logerror("%05X:read 80186 DMA%d upper source address\n", m_pc, (offset - 0x61) / 8); which = (offset - 0x61) / 8; return m_dma[which].source >> 16; case 0x62: case 0x6a: if (LOG_PORTS) logerror("%05X:read 80186 DMA%d lower dest address\n", m_pc, (offset - 0x62) / 8); which = (offset - 0x62) / 8; return m_dma[which].dest; case 0x63: case 0x6b: if (LOG_PORTS) logerror("%05X:read 80186 DMA%d upper dest address\n", m_pc, (offset - 0x63) / 8); which = (offset - 0x63) / 8; return m_dma[which].dest >> 16; case 0x64: case 0x6c: if (LOG_PORTS) logerror("%05X:read 80186 DMA%d transfer count\n", m_pc, (offset - 0x64) / 8); which = (offset - 0x64) / 8; return m_dma[which].count; case 0x65: case 0x6d: if (LOG_PORTS) logerror("%05X:read 80186 DMA%d control\n", m_pc, (offset - 0x65) / 8); which = (offset - 0x65) / 8; return m_dma[which].control; case 0x7f: return m_reloc; default: if (LOG_PORTS) logerror("%05X:read 80186 port %02X\n", m_pc, offset); break; } return 0x0000; } /************************************* * * 80186 internal I/O writes * *************************************/ WRITE16_MEMBER(i80186_cpu_device::internal_port_w) { int which; switch (offset) { case 0x10: if (LOG_PORTS) logerror("%05X:write interrupt vector = %04X\n", m_pc, data); m_intr.vector = data & 0xf8; break; case 0x11: if (LOG_PORTS) logerror("%05X:80186 EOI = %04X\n", m_pc, data); handle_eoi(data); update_interrupt_state(); break; case 0x12: if (LOG_PORTS) logerror("%05X:ERROR - write to 80186 interrupt poll = %04X\n", m_pc, data); break; case 0x13: if (LOG_PORTS) logerror("%05X:ERROR - write to 80186 interrupt poll status = %04X\n", m_pc, data); break; case 0x14: if (LOG_PORTS) logerror("%05X:80186 interrupt mask = %04X\n", m_pc, data); m_intr.timer[0] = (m_intr.timer[0] & ~0x08) | ((data << 3) & 0x08); m_intr.dma[0] = (m_intr.dma[0] & ~0x08) | ((data << 1) & 0x08); m_intr.dma[1] = (m_intr.dma[1] & ~0x08) | ((data << 0) & 0x08); if (BIT(m_reloc, 14)) { m_intr.timer[1] = (m_intr.timer[1] & ~0x08) | ((data >> 1) & 0x08); m_intr.timer[2] = (m_intr.timer[2] & ~0x08) | ((data >> 2) & 0x08); } else { m_intr.ext[0] = (m_intr.ext[0] & ~0x08) | ((data >> 1) & 0x08); m_intr.ext[1] = (m_intr.ext[1] & ~0x08) | ((data >> 2) & 0x08); m_intr.ext[2] = (m_intr.ext[2] & ~0x08) | ((data >> 3) & 0x08); m_intr.ext[3] = (m_intr.ext[3] & ~0x08) | ((data >> 4) & 0x08); } update_interrupt_state(); break; case 0x15: if (LOG_PORTS) logerror("%05X:80186 interrupt priority mask = %04X\n", m_pc, data); m_intr.priority_mask = data & 0x0007; update_interrupt_state(); break; case 0x16: if (LOG_PORTS) logerror("%05X:80186 interrupt in-service = %04X\n", m_pc, data); m_intr.in_service = data & 0x00ff; update_interrupt_state(); break; case 0x17: if (LOG_PORTS) logerror("%05X:80186 interrupt request = %04X\n", m_pc, data); m_intr.request = (m_intr.request & ~0x000c) | (data & 0x000c); update_interrupt_state(); break; case 0x18: if (LOG_PORTS) logerror("%05X:WARNING - wrote to 80186 interrupt status = %04X\n", m_pc, data); m_intr.status = (m_intr.status & ~0x8007) | (data & 0x8007); update_interrupt_state(); break; case 0x19: if (LOG_PORTS) logerror("%05X:80186 timer interrupt contol = %04X\n", m_pc, data); m_intr.timer[0] = data & 0x000f; update_interrupt_state(); break; case 0x1a: if (LOG_PORTS) logerror("%05X:80186 DMA 0 interrupt control = %04X\n", m_pc, data); m_intr.dma[0] = data & 0x000f; update_interrupt_state(); break; case 0x1b: if (LOG_PORTS) logerror("%05X:80186 DMA 1 interrupt control = %04X\n", m_pc, data); m_intr.dma[1] = data & 0x000f; update_interrupt_state(); break; case 0x1c: if (LOG_PORTS) logerror("%05X:80186 INT 0 interrupt control = %04X\n", m_pc, data); if (BIT(m_reloc, 14)) m_intr.timer[1] = data & 0x000f; else m_intr.ext[0] = data & 0x007f; update_interrupt_state(); break; case 0x1d: if (LOG_PORTS) logerror("%05X:80186 INT 1 interrupt control = %04X\n", m_pc, data); if (BIT(m_reloc, 14)) m_intr.timer[2] = data & 0x000f; else m_intr.ext[1] = data & 0x007f; update_interrupt_state(); break; case 0x1e: if (LOG_PORTS) logerror("%05X:80186 INT 2 interrupt control = %04X\n", m_pc, data); if (!BIT(m_reloc, 14)) m_intr.ext[2] = data & 0x001f; update_interrupt_state(); break; case 0x1f: if (LOG_PORTS) logerror("%05X:80186 INT 3 interrupt control = %04X\n", m_pc, data); if (!BIT(m_reloc, 14)) m_intr.ext[3] = data & 0x001f; update_interrupt_state(); break; case 0x28: case 0x2c: case 0x30: if (LOG_PORTS) logerror("%05X:80186 Timer %d count = %04X\n", m_pc, (offset - 0x28) / 4, data); which = (offset - 0x28) / 4; internal_timer_update(which, data, -1, -1, -1); break; case 0x29: case 0x2d: case 0x31: if (LOG_PORTS) logerror("%05X:80186 Timer %d max A = %04X\n", m_pc, (offset - 0x29) / 4, data); which = (offset - 0x29) / 4; internal_timer_update(which, -1, data, -1, -1); break; case 0x2a: case 0x2e: if (LOG_PORTS) logerror("%05X:80186 Timer %d max B = %04X\n", m_pc, (offset - 0x2a) / 4, data); which = (offset - 0x2a) / 4; internal_timer_update(which, -1, -1, data, -1); break; case 0x2b: case 0x2f: case 0x33: if (LOG_PORTS) logerror("%05X:80186 Timer %d control = %04X\n", m_pc, (offset - 0x2b) / 4, data); which = (offset - 0x2b) / 4; internal_timer_update(which, -1, -1, -1, data); break; case 0x50: if (LOG_PORTS) logerror("%05X:80186 upper chip select = %04X\n", m_pc, data); m_mem.upper = data | 0xc038; m_out_chip_select_func(0, m_mem.upper, 0xffff); break; case 0x51: if (LOG_PORTS) logerror("%05X:80186 lower chip select = %04X\n", m_pc, data); m_mem.lower = (data & 0x3fff) | 0x0038; m_out_chip_select_func(1, m_mem.lower, 0xffff); break; case 0x52: if (LOG_PORTS) logerror("%05X:80186 peripheral chip select = %04X\n", m_pc, data); m_mem.peripheral = data | 0x0038; m_out_chip_select_func(2, m_mem.peripheral, 0xffff); break; case 0x53: if (LOG_PORTS) logerror("%05X:80186 middle chip select = %04X\n", m_pc, data); m_mem.middle = data | 0x01f8; m_out_chip_select_func(3, m_mem.middle, 0xffff); break; case 0x54: if (LOG_PORTS) logerror("%05X:80186 middle P chip select = %04X\n", m_pc, data); m_mem.middle_size = data | 0x8038; m_out_chip_select_func(4, m_mem.middle_size, 0xffff); break; case 0x60: case 0x68: if (LOG_PORTS) logerror("%05X:80186 DMA%d lower source address = %04X\n", m_pc, (offset - 0x60) / 8, data); which = (offset - 0x60) / 8; m_dma[which].source = (m_dma[which].source & ~0x0ffff) | (data & 0x0ffff); break; case 0x61: case 0x69: if (LOG_PORTS) logerror("%05X:80186 DMA%d upper source address = %04X\n", m_pc, (offset - 0x61) / 8, data); which = (offset - 0x61) / 8; m_dma[which].source = (m_dma[which].source & ~0xf0000) | ((data << 16) & 0xf0000); break; case 0x62: case 0x6a: if (LOG_PORTS) logerror("%05X:80186 DMA%d lower dest address = %04X\n", m_pc, (offset - 0x62) / 8, data); which = (offset - 0x62) / 8; m_dma[which].dest = (m_dma[which].dest & ~0x0ffff) | (data & 0x0ffff); break; case 0x63: case 0x6b: if (LOG_PORTS) logerror("%05X:80186 DMA%d upper dest address = %04X\n", m_pc, (offset - 0x63) / 8, data); which = (offset - 0x63) / 8; m_dma[which].dest = (m_dma[which].dest & ~0xf0000) | ((data << 16) & 0xf0000); break; case 0x64: case 0x6c: if (LOG_PORTS) logerror("%05X:80186 DMA%d transfer count = %04X\n", m_pc, (offset - 0x64) / 8, data); which = (offset - 0x64) / 8; m_dma[which].count = data; break; case 0x65: case 0x6d: if (LOG_PORTS) logerror("%05X:80186 DMA%d control = %04X\n", m_pc, (offset - 0x65) / 8, data); which = (offset - 0x65) / 8; update_dma_control(which, data); if((m_dma[which].control & (SYNC_MASK | ST_STOP | TIMER_DRQ)) == ST_STOP) { // TODO: don't do this while(m_dma[which].control & ST_STOP) drq_callback(which); } break; case 0x7f: // 80188 byte output to this port is *not* masked! if (LOG_PORTS) logerror("%05X:80186 relocation register = %04X\n", m_pc, data); m_reloc = data; break; default: if (LOG_PORTS) logerror("%05X:80186 port %02X = %04X\n", m_pc, offset, data); break; } }