244 lines
9.3 KiB
C
244 lines
9.3 KiB
C
/* chip_i8259.c: system independent implementation of PIC chip
|
|
|
|
Copyright (c) 2009-2010 Holger Veit
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
Holger Veit BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Holger Veit et al shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Holger Veit et al.
|
|
|
|
22-Jan-10 HV Initial version
|
|
03-Jun-10 HV Repair POLL function (defective FDC interrupt handling in SAGEBIOS)
|
|
*/
|
|
|
|
#include "sim_defs.h"
|
|
#include "m68k_cpu.h"
|
|
#include "chip_defs.h"
|
|
|
|
/* Debug Flags */
|
|
DEBTAB i8259_dt[] = {
|
|
{ "READ", DBG_PIC_RD },
|
|
{ "WRITE", DBG_PIC_WR },
|
|
{ "IRQIN", DBG_PIC_II },
|
|
{ "IRQOUT", DBG_PIC_IO },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
static int32 priomask[] = { 0x0000,0x4000,0x6000,0x7000,0x7800,0x7c00,0x7e00,0x7f00 };
|
|
|
|
t_stat i8259_io(IOHANDLER* ioh,uint32* value,uint32 rw,uint32 mask)
|
|
{
|
|
int port = ioh->offset;
|
|
I8259* chip = (I8259*)ioh->ctxt;
|
|
if (rw==MEM_WRITE) {
|
|
return chip->write ? chip->write(chip,port,*value) : i8259_write(chip,port,*value);
|
|
} else {
|
|
return chip->read ? chip->read(chip,port,value) : i8259_read(chip,port,value);
|
|
}
|
|
}
|
|
|
|
t_stat i8259_write(I8259* chip,int addr,uint32 value)
|
|
{
|
|
int i, bit;
|
|
|
|
#if 0
|
|
TRACE_PRINT2(DBG_PIC_WR,"WR addr=%d data=0x%x",addr,value);
|
|
#endif
|
|
if (addr==1) {
|
|
switch (chip->state) {
|
|
default:
|
|
case 0: /* after reset */
|
|
sim_printf("PIC: write addr=1 without initialization\n");
|
|
return SCPE_IOERR;
|
|
case 1: /* expect ICW2 */
|
|
TRACE_PRINT2(DBG_PIC_WR,"WR ICW2: addr=%d data=0x%x",addr,value);
|
|
chip->icw2 = value;
|
|
if (chip->icw1 & I8259_ICW1_SNGL) {
|
|
chip->state = (chip->icw1 & I8259_ICW1_IC4) ? 4 : 5;
|
|
} else {
|
|
/* attempt to program cascade mode */
|
|
sim_printf("PIC: attempt to program chip for cascade mode - not wired for this!\n");
|
|
chip->state = 0;
|
|
return SCPE_IOERR;
|
|
}
|
|
break;
|
|
case 4: /* expect ICW4 */
|
|
TRACE_PRINT2(DBG_PIC_WR,"WR ICW4 addr=%d data=0x%x",addr,value);
|
|
chip->icw4 = value;
|
|
if (chip->icw4 & I8259_ICW4_AEOI) {
|
|
sim_printf("PIC: attempt to program chip for AEOI mode - not wired for this!\n");
|
|
return SCPE_IOERR;
|
|
}
|
|
if (chip->icw4 & I8259_ICW4_BUF) {
|
|
sim_printf("PIC: attempt to program chip for buffered mode - not wired for this!\n");
|
|
return SCPE_IOERR;
|
|
}
|
|
if (chip->icw4 & I8259_ICW4_SFNM) {
|
|
sim_printf("PIC: attempt to program chip for spc nested mode - not wired for this!\n");
|
|
return SCPE_IOERR;
|
|
}
|
|
chip->state = 5;
|
|
break;
|
|
case 5: /* ready to accept interrupt requests and ocw commands */
|
|
/* ocw1 */
|
|
TRACE_PRINT2(DBG_PIC_WR,"WR IMR addr=%d data=0x%x",addr,value);
|
|
chip->imr = value;
|
|
break;
|
|
}
|
|
} else {
|
|
if (value & I8259_ICW1) { /* state initialization sequence */
|
|
TRACE_PRINT2(DBG_PIC_WR,"WR ICW1 addr=%d data=0x%x",addr,value);
|
|
chip->icw1 = value;
|
|
chip->state = 1;
|
|
chip->rmode = 0;
|
|
chip->prio = 7;
|
|
if ((chip->icw1 & I8259_ICW1_IC4)==0) chip->icw4 = 0;
|
|
|
|
return SCPE_OK;
|
|
} else { /* ocw2 and ocw3 */
|
|
if (value & I8259_OCW3) { /* ocw3 */
|
|
TRACE_PRINT2(DBG_PIC_WR,"WR OCW3 addr=%d data=0x%x",addr,value);
|
|
if (value & I8259_OCW3_ESMM) {
|
|
sim_printf("PIC: ESMM not yet supported\n");
|
|
return STOP_IMPL;
|
|
}
|
|
if (value & I8259_OCW3_POLL) {
|
|
chip->rmode |= 2;
|
|
return SCPE_OK;
|
|
}
|
|
if (value & I8259_OCW3_RR)
|
|
chip->rmode = (value & I8259_OCW3_RIS) ? 1 /*isr*/ : 0; /* irr */
|
|
} else { /* ocw2 */
|
|
TRACE_PRINT2(DBG_PIC_WR,"WR OCW2 addr=%d data=0x%x",addr,value);
|
|
switch (value & I8259_OCW2_MODE) {
|
|
case 0xa0: /* rotate on nospecific eoi */
|
|
case 0x20: /* nonspecific eoi */
|
|
bit = 1 << (7 - chip->prio);
|
|
for (i=0; i<7; i++) {
|
|
if (chip->isr & bit) break;
|
|
bit = bit << 1; if (bit==0x100) bit = 1;
|
|
}
|
|
chip->isr &= ~bit; break;
|
|
if ((value & I8259_OCW2_MODE) == 0xa0) {
|
|
chip->prio = 7 - i + chip->prio; if (chip->prio>7) chip->prio -= 8;
|
|
}
|
|
break;
|
|
case 0xe0: /* rotate on specific eoi */
|
|
chip->prio = 7 - (value & 7) + chip->prio; if (chip->prio>7) chip->prio -= 8;
|
|
/*fallthru*/
|
|
case 0x60: /* specific eoi */
|
|
bit = 1 << (value & 7);
|
|
chip->isr = chip->isr & ~bit & 0xff;
|
|
break;
|
|
case 0x80: /* rotate in autoeoi (set) */
|
|
case 0x00: /* rotate in autoeoi (clear) */
|
|
sim_printf("PIC: AEOI not supported\n");
|
|
return SCPE_IOERR;
|
|
case 0xc0: /* set prio */
|
|
chip->prio = value & 7;
|
|
return SCPE_OK;
|
|
case 0x40: /* nop */
|
|
break;
|
|
default:
|
|
return SCPE_IERR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat i8259_read(I8259* chip,int addr, uint32* value)
|
|
{
|
|
int i, bit, num;
|
|
|
|
if (addr) {
|
|
*value = chip->imr;
|
|
} else {
|
|
switch (chip->rmode) {
|
|
case 0:
|
|
TRACE_PRINT2(DBG_PIC_RD,"Read IRR addr=%d data=0x%x",addr,chip->irr);
|
|
*value = chip->irr; break;
|
|
case 1:
|
|
TRACE_PRINT2(DBG_PIC_RD,"Read ISR addr=%d data=0x%x",addr,chip->irr);
|
|
*value = chip->isr; break;
|
|
case 2:
|
|
case 3:
|
|
TRACE_PRINT2(DBG_PIC_RD,"Read POLL addr=%d data=0x%x",addr,chip->irr);
|
|
num = chip->prio;
|
|
bit = 1 << chip->prio;
|
|
for (i=0; i<8; i++,num--) {
|
|
if (chip->isr & bit) {
|
|
*value = 0x80 | (num & 7);
|
|
TRACE_PRINT2(DBG_PIC_RD,"Read POLL addr=%d data=0x%x",addr,*value);
|
|
return SCPE_OK;
|
|
}
|
|
bit >>= 1;
|
|
if (bit==0) { bit = 0x80; num = 7; }
|
|
}
|
|
chip->rmode &= ~2;
|
|
}
|
|
}
|
|
#if 0
|
|
TRACE_PRINT2(DBG_PIC_RD,"Read addr=%d data=0x%x",addr,*value);
|
|
#endif
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat i8259_raiseint(I8259* chip,int level)
|
|
{
|
|
int32 bit, isr, myprio;
|
|
|
|
TRACE_PRINT1(DBG_PIC_II,"Request INT level=%d",level);
|
|
|
|
if (chip->state != 5) return SCPE_OK; /* not yet initialized, ignore interrupts */
|
|
bit = 1<<level;
|
|
if (chip->imr & bit) return SCPE_OK; /* inhibited */
|
|
chip->isr = (chip->isr | bit) & 0xff; /* request this interrupt level */
|
|
|
|
/* bit7=prio7 => bitN = prioN
|
|
bit7=prio6 => bitN = prioN-1
|
|
...
|
|
bit7=prio0 => bitN = prioN-7
|
|
*/
|
|
isr = (chip->isr<<8) | chip->isr; /* simple rotation */
|
|
isr = isr << (7-level); /* shift level bit into bit 15 */
|
|
myprio = chip->prio - 7 + level; if (myprio < 0) myprio += 8;
|
|
if (!(isr & priomask[myprio])) { /* higher interrupt is pending */
|
|
if (chip->autoint) {
|
|
TRACE_PRINT1(DBG_PIC_IO,"Raise AUTOINT level=%d",chip->intlevel);
|
|
return m68k_raise_autoint(chip->intlevel);
|
|
} else {
|
|
TRACE_PRINT2(DBG_PIC_IO,"Raise VECTORINT level=%d vector=%x",chip->intlevel,chip->intvector);
|
|
return m68k_raise_vectorint(chip->intlevel,chip->intvector);
|
|
}
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat i8259_reset(I8259* chip)
|
|
{
|
|
chip->autoint = TRUE;
|
|
chip->intlevel = 1;
|
|
chip->intvector = 0;
|
|
chip->state = 0;
|
|
chip->rmode = 0;
|
|
chip->imr = 0;
|
|
return SCPE_OK;
|
|
}
|