mirror of
https://github.com/holub/mame
synced 2025-06-01 02:21:48 +03:00
1687 lines
46 KiB
C
1687 lines
46 KiB
C
// Peripheral code from rmnimbus driver by Phill Harvey-Smith which is
|
|
// based on the Leland sound driver by Aaron Giles and Paul Leaman
|
|
|
|
#include "i186.h"
|
|
#include "debugger.h"
|
|
#include "i86inline.h"
|
|
|
|
#define LATCH_INTS 1
|
|
#define LOG_PORTS 0
|
|
#define LOG_INTERRUPTS 0
|
|
#define LOG_INTERRUPTS_EXT 0
|
|
#define LOG_TIMER 0
|
|
#define LOG_OPTIMIZATION 0
|
|
#define LOG_DMA 0
|
|
#define CPU_RESUME_TRIGGER 7123
|
|
|
|
/* 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 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 */
|
|
};
|
|
|
|
const device_type I80186 = &device_creator<i80186_cpu_device>;
|
|
const device_type I80188 = &device_creator<i80188_cpu_device>;
|
|
|
|
i80188_cpu_device::i80188_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
|
: i80186_cpu_device(mconfig, I80188, "I80188", tag, owner, clock, "i80188", __FILE__, 8)
|
|
{
|
|
memcpy(m_timing, m_i80186_timing, sizeof(m_i80186_timing));
|
|
m_fetch_xor = 0;
|
|
}
|
|
|
|
i80186_cpu_device::i80186_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
|
: i8086_common_cpu_device(mconfig, I80186, "I80186", tag, owner, clock, "i80186", __FILE__)
|
|
, m_program_config("program", ENDIANNESS_LITTLE, 16, 20, 0)
|
|
, m_io_config("io", ENDIANNESS_LITTLE, 16, 16, 0)
|
|
, m_read_slave_ack_func(*this)
|
|
, m_out_chip_select_func(*this)
|
|
, m_out_tmrout0_func(*this)
|
|
, m_out_tmrout1_func(*this)
|
|
{
|
|
memcpy(m_timing, m_i80186_timing, sizeof(m_i80186_timing));
|
|
m_fetch_xor = BYTE_XOR_LE(0);
|
|
}
|
|
|
|
i80186_cpu_device::i80186_cpu_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source, int data_bus_size)
|
|
: i8086_common_cpu_device(mconfig, type, name, tag, owner, clock, shortname, source)
|
|
, m_program_config("program", 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)
|
|
{
|
|
}
|
|
|
|
UINT8 i80186_cpu_device::fetch_op()
|
|
{
|
|
UINT8 data;
|
|
data = m_direct->read_decrypted_byte(pc(), m_fetch_xor);
|
|
m_ip++;
|
|
return data;
|
|
}
|
|
|
|
UINT8 i80186_cpu_device::fetch()
|
|
{
|
|
UINT8 data;
|
|
data = m_direct->read_raw_byte(pc(), m_fetch_xor);
|
|
m_ip++;
|
|
return data;
|
|
}
|
|
|
|
void i80186_cpu_device::execute_run()
|
|
{
|
|
while(m_icount > 0 )
|
|
{
|
|
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( this, pc() );
|
|
|
|
UINT8 op = fetch_op();
|
|
|
|
switch(op)
|
|
{
|
|
case 0x60: // i_pusha
|
|
{
|
|
UINT32 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 low,high,tmp;
|
|
m_modrm = fetch();
|
|
low = GetRMWord();
|
|
high = GetnextRMWord();
|
|
tmp = RegWord();
|
|
if (tmp<low || tmp>high)
|
|
interrupt(5);
|
|
CLK(BOUND);
|
|
logerror("%s: %06x: bound %04x high %04x low %04x tmp\n", tag(), pc(), high, low, tmp);
|
|
}
|
|
break;
|
|
|
|
case 0x68: // i_push_d16
|
|
PUSH( fetch_word() );
|
|
CLK(PUSH_IMM);
|
|
break;
|
|
|
|
case 0x69: // i_imul_d16
|
|
{
|
|
UINT32 tmp;
|
|
DEF_r16w();
|
|
tmp = fetch_word();
|
|
m_dst = (INT32)((INT16)m_src)*(INT32)((INT16)tmp);
|
|
m_CarryVal = m_OverVal = (((INT32)m_dst) >> 15 != 0) && (((INT32)m_dst) >> 15 != -1);
|
|
RegWord(m_dst);
|
|
CLKM(IMUL_RRI16, IMUL_RMI16);
|
|
}
|
|
break;
|
|
|
|
case 0x6a: // i_push_d8
|
|
PUSH( (UINT16)((INT16)((INT8)fetch())) );
|
|
CLK(PUSH_IMM);
|
|
break;
|
|
|
|
case 0x6b: // i_imul_d8
|
|
{
|
|
UINT32 src2;
|
|
DEF_r16w();
|
|
src2= (UINT16)((INT16)((INT8)fetch()));
|
|
m_dst = (INT32)((INT16)m_src)*(INT32)((INT16)src2);
|
|
m_CarryVal = m_OverVal = (((INT32)m_dst) >> 15 != 0) && (((INT32)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("%s: %06x: Mov Sreg - Invalid register\n", tag(), pc());
|
|
m_ip = m_prev_ip;
|
|
interrupt(6);
|
|
}
|
|
break;
|
|
|
|
case 0xc0: // i_rotshft_bd8
|
|
{
|
|
UINT8 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 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 nb = fetch();
|
|
UINT32 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; i<level; i++)
|
|
{
|
|
PUSH( GetMemW(SS,m_regs.w[BP] - i*2) );
|
|
}
|
|
if (level)
|
|
{
|
|
PUSH(m_regs.w[BP]);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0xc9: // i_leave
|
|
m_regs.w[SP] = m_regs.w[BP];
|
|
m_regs.w[BP] = POP();
|
|
CLK(LEAVE);
|
|
break;
|
|
|
|
case 0xd2: // i_rotshft_bcl
|
|
{
|
|
UINT8 c;
|
|
|
|
m_modrm = fetch();
|
|
m_src = GetRMByte();
|
|
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_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 0xd3: // i_rotshft_wcl
|
|
{
|
|
UINT8 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 0xf2: // i_repne
|
|
case 0xf3:
|
|
{
|
|
bool pass = false;
|
|
UINT8 next = repx_op();
|
|
UINT16 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;
|
|
}
|
|
if(!pass)
|
|
{
|
|
if(c)
|
|
m_ip = m_prev_ip;
|
|
break;
|
|
}
|
|
}
|
|
|
|
default:
|
|
if(!common_op(op))
|
|
{
|
|
m_icount -= 10; // UD fault timing?
|
|
logerror("%s: %06x: Invalid Opcode %02x\n", tag(), 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] ).callimport().callexport().formatstr("%04X");
|
|
state_add( I8086_CS, "CS", m_sregs[CS] ).callimport().callexport().formatstr("%04X");
|
|
state_add( I8086_SS, "SS", m_sregs[SS] ).callimport().callexport().formatstr("%04X");
|
|
state_add( I8086_DS, "DS", m_sregs[DS] ).callimport().callexport().formatstr("%04X");
|
|
state_add( I8086_VECTOR, "V", m_int_vector).callimport().callexport().formatstr("%02X");
|
|
|
|
state_add(STATE_GENPC, "curpc", m_pc).callimport().callexport().formatstr("%05X");
|
|
|
|
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[0].time_timer_active));
|
|
save_item(NAME(m_timer[0].last_time));
|
|
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[1].time_timer_active));
|
|
save_item(NAME(m_timer[1].last_time));
|
|
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_timer[2].time_timer_active));
|
|
save_item(NAME(m_timer[2].last_time));
|
|
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.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_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));
|
|
|
|
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_timer[0].time_timer = timer_alloc(TIMER_TIME0);
|
|
m_timer[1].time_timer = timer_alloc(TIMER_TIME1);
|
|
m_timer[2].time_timer = timer_alloc(TIMER_TIME2);
|
|
m_dma[0].finish_timer = timer_alloc(TIMER_DMA0);
|
|
m_dma[1].finish_timer = timer_alloc(TIMER_DMA1);
|
|
|
|
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();
|
|
}
|
|
|
|
void i80186_cpu_device::device_reset()
|
|
{
|
|
i8086_common_cpu_device::device_reset();
|
|
/* reset the interrupt state */
|
|
m_intr.priority_mask = 0x0007;
|
|
m_intr.timer = 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_reloc = 0x20ff;
|
|
m_dma[0].drq_delay = false;
|
|
m_dma[1].drq_delay = false;
|
|
m_dma[0].drq_state = false;
|
|
m_dma[1].drq_state = false;
|
|
m_timer[0].control = 0;
|
|
m_timer[1].control = 0;
|
|
m_timer[2].control = 0;
|
|
|
|
set_irq_acknowledge_callback(device_irq_acknowledge_delegate(FUNC(i80186_cpu_device::int_callback),this));
|
|
}
|
|
|
|
UINT8 i80186_cpu_device::read_port_byte(UINT16 port)
|
|
{
|
|
if(!(m_reloc & 0x1000) && (port >> 8) == (m_reloc & 0xff))
|
|
{
|
|
UINT16 ret = internal_port_r(*m_io, (port >> 1) - ((m_reloc & 0xff) << 7), (port & 1) ? 0xff00 : 0x00ff);
|
|
return (port & 1) ? (ret >> 8) : (ret & 0xff);
|
|
}
|
|
return m_io->read_byte(port);
|
|
}
|
|
|
|
UINT16 i80186_cpu_device::read_port_word(UINT16 port)
|
|
{
|
|
if(!(m_reloc & 0x1000) && (port >> 8) == (m_reloc & 0xff))
|
|
{
|
|
if(port & 1)
|
|
{
|
|
UINT8 low = read_port_byte(port);
|
|
return read_port_byte(port + 1) << 8 | low;
|
|
}
|
|
return internal_port_r(*m_io, (port >> 1) - ((m_reloc & 0xff) << 7));
|
|
}
|
|
return m_io->read_word_unaligned(port);
|
|
}
|
|
|
|
void i80186_cpu_device::write_port_byte(UINT16 port, UINT8 data)
|
|
{
|
|
if(!(m_reloc & 0x1000) && (port >> 8) == (m_reloc & 0xff))
|
|
internal_port_w(*m_io, (port >> 1) - ((m_reloc & 0xff) << 7), (port & 1) ? (data << 8) : data, (port & 1) ? 0xff00 : 0x00ff);
|
|
else
|
|
m_io->write_byte(port, data);
|
|
}
|
|
|
|
void i80186_cpu_device::write_port_word(UINT16 port, UINT16 data)
|
|
{
|
|
if(!(m_reloc & 0x1000) && (port >> 8) == (m_reloc & 0xff))
|
|
{
|
|
if(port & 1)
|
|
{
|
|
write_port_byte(port, data & 0xff);
|
|
write_port_byte(port + 1, data >> 8);
|
|
}
|
|
else
|
|
internal_port_w(*m_io, (port >> 1) - ((m_reloc & 0xff) << 7), data);
|
|
}
|
|
else
|
|
m_io->write_word_unaligned(port, data);
|
|
}
|
|
|
|
/*************************************
|
|
*
|
|
* 80186 interrupt controller
|
|
*
|
|
*************************************/
|
|
IRQ_CALLBACK_MEMBER(i80186_cpu_device::int_callback)
|
|
{
|
|
UINT8 vector;
|
|
UINT16 old;
|
|
UINT16 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 LATCH_INTS
|
|
m_intr.request &= ~m_intr.ack_mask;
|
|
#else
|
|
m_intr.request &= ~(m_intr.ack_mask & 0x0f);
|
|
#endif
|
|
|
|
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 (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;
|
|
}
|
|
}
|
|
m_intr.ack_mask = 0;
|
|
|
|
/* a request no longer pending */
|
|
m_intr.poll_status &= ~0x8000;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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 ((m_intr.timer & 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))
|
|
{
|
|
new_vector = 0x0a + IntNo;
|
|
|
|
/* set the clear mask and generate the int */
|
|
m_intr.ack_mask = 0x0004 << IntNo;
|
|
goto generate_int;
|
|
}
|
|
}
|
|
|
|
/* 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))
|
|
{
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
|
|
generate_int:
|
|
/* generate the appropriate interrupt */
|
|
m_intr.poll_status = 0x8000 | new_vector;
|
|
if (!m_intr.pending)
|
|
set_input_line(0, ASSERT_LINE);
|
|
m_intr.pending = 1;
|
|
machine().scheduler().trigger(CPU_RESUME_TRIGGER);
|
|
if (LOG_OPTIMIZATION) logerror(" - trigger due to interrupt pending\n");
|
|
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", 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 ((m_intr.timer & 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Trigger an external interrupt, optionally supplying the vector to take */
|
|
void i80186_cpu_device::external_int(UINT16 intno, int state, UINT8 vector)
|
|
{
|
|
if (LOG_INTERRUPTS_EXT) logerror("generating external int %02X, vector %02X\n",intno,vector);
|
|
|
|
if(!state)
|
|
return;
|
|
|
|
// Turn on the requested request bit and handle interrupt
|
|
m_intr.request |= (0x010 << 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;
|
|
struct 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, reset */
|
|
if (t->control & 0x0001)
|
|
{
|
|
int count;
|
|
if((t->control & 2) && (which != 2))
|
|
{
|
|
count = t->active_count ? t->maxA : t->maxB;
|
|
t->active_count = !t->active_count;
|
|
}
|
|
else
|
|
count = t->maxA;
|
|
|
|
count = count ? count : 0x10000;
|
|
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);
|
|
break;
|
|
}
|
|
case TIMER_DMA0:
|
|
case TIMER_DMA1:
|
|
{
|
|
int which = param;
|
|
struct dma_state *d = &m_dma[which];
|
|
|
|
d->drq_delay = false;
|
|
if(d->drq_state)
|
|
drq_callback(which);
|
|
break;
|
|
}
|
|
case TIMER_TIME0:
|
|
case TIMER_TIME1:
|
|
case TIMER_TIME2:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void i80186_cpu_device::internal_timer_sync(int which)
|
|
{
|
|
struct timer_state *t = &m_timer[which];
|
|
|
|
/* if we have a timing timer running, adjust the count */
|
|
if (t->time_timer_active && !(t->control & 0x0c))
|
|
{
|
|
t->last_time = t->time_timer->elapsed();
|
|
}
|
|
}
|
|
|
|
void i80186_cpu_device::inc_timer(int which)
|
|
{
|
|
struct 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, NULL);
|
|
}
|
|
else if(t->count == t->maxA)
|
|
device_timer(*t->int_timer, which, which, NULL);
|
|
}
|
|
|
|
void i80186_cpu_device::internal_timer_update(int which,int new_count,int new_maxA,int new_maxB,int new_control)
|
|
{
|
|
struct 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 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", 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);
|
|
|
|
/* nuke the timer and force the interrupt timer to be recomputed */
|
|
t->time_timer->adjust(attotime::never, which);
|
|
t->time_timer_active = 0;
|
|
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))
|
|
{
|
|
/* start the timing */
|
|
t->time_timer->adjust(attotime::never, which);
|
|
t->time_timer_active = 1;
|
|
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;
|
|
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)
|
|
{
|
|
struct 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", 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)
|
|
{
|
|
struct dma_state *dma = &m_dma[which];
|
|
|
|
UINT16 dma_word;
|
|
UINT8 dma_byte;
|
|
UINT8 incdec_size;
|
|
|
|
if(dma->drq_delay)
|
|
return;
|
|
|
|
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", 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(dma->source);
|
|
dest_space->write_word(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->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();
|
|
}
|
|
|
|
// dma->finish_timer->adjust(attotime::from_hz(clock()/8), 0);
|
|
// dma->drq_delay = true;
|
|
}
|
|
|
|
READ16_MEMBER(i80186_cpu_device::internal_port_r)
|
|
{
|
|
int temp, which;
|
|
|
|
switch (offset)
|
|
{
|
|
case 0x11:
|
|
logerror("%05X:ERROR - read from 80186 EOI\n", pc());
|
|
break;
|
|
|
|
case 0x12:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 interrupt poll\n", pc());
|
|
if (m_intr.poll_status & 0x8000)
|
|
int_callback(*this, 0);
|
|
return m_intr.poll_status;
|
|
|
|
case 0x13:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 interrupt poll status\n", pc());
|
|
return m_intr.poll_status;
|
|
|
|
case 0x14:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 interrupt mask\n", pc());
|
|
temp = (m_intr.timer >> 3) & 0x01;
|
|
temp |= (m_intr.dma[0] >> 1) & 0x04;
|
|
temp |= (m_intr.dma[1] >> 0) & 0x08;
|
|
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", pc());
|
|
return m_intr.priority_mask;
|
|
|
|
case 0x16:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 interrupt in-service\n", pc());
|
|
return m_intr.in_service;
|
|
|
|
case 0x17:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 interrupt request\n", 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", pc());
|
|
return m_intr.status;
|
|
|
|
case 0x19:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 timer interrupt control\n", pc());
|
|
return m_intr.timer;
|
|
|
|
case 0x1a:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 DMA 0 interrupt control\n", pc());
|
|
return m_intr.dma[0];
|
|
|
|
case 0x1b:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 DMA 1 interrupt control\n", pc());
|
|
return m_intr.dma[1];
|
|
|
|
case 0x1c:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 INT 0 interrupt control\n", pc());
|
|
return m_intr.ext[0];
|
|
|
|
case 0x1d:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 INT 1 interrupt control\n", pc());
|
|
return m_intr.ext[1];
|
|
|
|
case 0x1e:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 INT 2 interrupt control\n", pc());
|
|
return m_intr.ext[2];
|
|
|
|
case 0x1f:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 INT 3 interrupt control\n", pc());
|
|
return m_intr.ext[3];
|
|
|
|
case 0x28:
|
|
case 0x2c:
|
|
case 0x30:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 Timer %d count\n", pc(), (offset - 0x28) / 4);
|
|
which = (offset - 0x28) / 4;
|
|
if (!(offset & 1))
|
|
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", pc(), (offset - 0x29) / 4);
|
|
which = (offset - 0x29) / 4;
|
|
return m_timer[which].maxA;
|
|
|
|
case 0x2a:
|
|
case 0x2e:
|
|
logerror("%05X:read 80186 Timer %d max B\n", 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", 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", pc());
|
|
return m_mem.upper;
|
|
|
|
case 0x51:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 lower chip select\n", pc());
|
|
return m_mem.lower;
|
|
|
|
case 0x52:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 peripheral chip select\n", pc());
|
|
return m_mem.peripheral;
|
|
|
|
case 0x53:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 middle chip select\n", pc());
|
|
return m_mem.middle;
|
|
|
|
case 0x54:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 middle P chip select\n", pc());
|
|
return m_mem.middle_size;
|
|
|
|
case 0x60:
|
|
case 0x68:
|
|
if (LOG_PORTS) logerror("%05X:read 80186 DMA%d lower source address\n", 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", 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", 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", 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", 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", pc(), (offset - 0x65) / 8);
|
|
which = (offset - 0x65) / 8;
|
|
return m_dma[which].control;
|
|
|
|
case 0x7f:
|
|
return m_reloc;
|
|
|
|
default:
|
|
logerror("%05X:read 80186 port %02X\n", pc(), offset);
|
|
break;
|
|
}
|
|
return 0x00;
|
|
}
|
|
|
|
/*************************************
|
|
*
|
|
* 80186 internal I/O writes
|
|
*
|
|
*************************************/
|
|
|
|
WRITE16_MEMBER(i80186_cpu_device::internal_port_w)
|
|
{
|
|
int which;
|
|
|
|
switch (offset)
|
|
{
|
|
case 0x11:
|
|
if (LOG_PORTS) logerror("%05X:80186 EOI = %04X\n", pc(), data);
|
|
handle_eoi(0x8000);
|
|
update_interrupt_state();
|
|
break;
|
|
|
|
case 0x12:
|
|
logerror("%05X:ERROR - write to 80186 interrupt poll = %04X\n", pc(), data);
|
|
break;
|
|
|
|
case 0x13:
|
|
logerror("%05X:ERROR - write to 80186 interrupt poll status = %04X\n", pc(), data);
|
|
break;
|
|
|
|
case 0x14:
|
|
if (LOG_PORTS) logerror("%05X:80186 interrupt mask = %04X\n", pc(), data);
|
|
m_intr.timer = (m_intr.timer & ~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);
|
|
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", 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", pc(), data);
|
|
m_intr.in_service = data & 0x00ff;
|
|
update_interrupt_state();
|
|
break;
|
|
|
|
case 0x17:
|
|
if (LOG_PORTS) logerror("%05X:80186 interrupt request = %04X\n", pc(), data);
|
|
m_intr.request = (m_intr.request & ~0x00c0) | (data & 0x00c0);
|
|
update_interrupt_state();
|
|
break;
|
|
|
|
case 0x18:
|
|
if (LOG_PORTS) logerror("%05X:WARNING - wrote to 80186 interrupt status = %04X\n", 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", pc(), data);
|
|
m_intr.timer = data & 0x000f;
|
|
break;
|
|
|
|
case 0x1a:
|
|
if (LOG_PORTS) logerror("%05X:80186 DMA 0 interrupt control = %04X\n", pc(), data);
|
|
m_intr.dma[0] = data & 0x000f;
|
|
break;
|
|
|
|
case 0x1b:
|
|
if (LOG_PORTS) logerror("%05X:80186 DMA 1 interrupt control = %04X\n", pc(), data);
|
|
m_intr.dma[1] = data & 0x000f;
|
|
break;
|
|
|
|
case 0x1c:
|
|
if (LOG_PORTS) logerror("%05X:80186 INT 0 interrupt control = %04X\n", pc(), data);
|
|
m_intr.ext[0] = data & 0x007f;
|
|
break;
|
|
|
|
case 0x1d:
|
|
if (LOG_PORTS) logerror("%05X:80186 INT 1 interrupt control = %04X\n", pc(), data);
|
|
m_intr.ext[1] = data & 0x007f;
|
|
break;
|
|
|
|
case 0x1e:
|
|
if (LOG_PORTS) logerror("%05X:80186 INT 2 interrupt control = %04X\n", pc(), data);
|
|
m_intr.ext[2] = data & 0x001f;
|
|
break;
|
|
|
|
case 0x1f:
|
|
if (LOG_PORTS) logerror("%05X:80186 INT 3 interrupt control = %04X\n", pc(), data);
|
|
m_intr.ext[3] = data & 0x001f;
|
|
break;
|
|
|
|
case 0x28:
|
|
case 0x2c:
|
|
case 0x30:
|
|
if (LOG_PORTS) logerror("%05X:80186 Timer %d count = %04X\n", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", pc(), (offset - 0x65) / 8, data);
|
|
which = (offset - 0x65) / 8;
|
|
update_dma_control(which, data);
|
|
break;
|
|
|
|
case 0x7f:
|
|
if (LOG_PORTS) logerror("%05X:80186 relocation register = %04X\n", pc(), data);
|
|
if((data & 0x1fff) != (m_reloc & 0x1fff))
|
|
{
|
|
UINT32 newmap = (data & 0xfff) << 8;
|
|
UINT32 oldmap = (m_reloc & 0xfff) << 8;
|
|
if(!(data & 0x1000) || ((data & 0x1000) && (m_reloc & 0x1000)))
|
|
m_program->unmap_readwrite(oldmap, oldmap + 0xff);
|
|
if(data & 0x1000) // TODO: make work with 80188 if needed
|
|
m_program->install_readwrite_handler(newmap, newmap + 0xff, read16_delegate(FUNC(i80186_cpu_device::internal_port_r), this), write16_delegate(FUNC(i80186_cpu_device::internal_port_w), this));
|
|
}
|
|
m_reloc = data;
|
|
|
|
break;
|
|
|
|
default:
|
|
logerror("%05X:80186 port %02X = %04X\n", pc(), offset, data);
|
|
break;
|
|
}
|
|
}
|