/************************************************************************* | |
* * | |
* $Id: s100_ss1.c 1997 2008-07-18 05:29:52Z hharte $ * | |
* * | |
* Copyright (c) 2007-2008 Howard M. Harte. * | |
* http://www.hartetec.com * | |
* * | |
* 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 HOWARD M. HARTE 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 Howard M. Harte shall * | |
* not be used in advertising or otherwise to promote the sale, use or * | |
* other dealings in this Software without prior written authorization * | |
* Howard M. Harte. * | |
* * | |
* SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * | |
* * | |
* Module Description: * | |
* CompuPro System Support 1 module for SIMH. * | |
* Note this does not include the Boot ROM on the System Support 1 Card * | |
* * | |
* Environment: * | |
* User mode only * | |
* * | |
*************************************************************************/ | |
/*#define DBG_MSG */ | |
#include "altairz80_defs.h" | |
#if defined (_WIN32) | |
#include <windows.h> | |
#endif | |
#include <time.h> | |
#ifdef DBG_MSG | |
#define DBG_PRINT(args) printf args | |
#else | |
#define DBG_PRINT(args) | |
#endif | |
/* Debug flags */ | |
#define ERROR_MSG (1 << 0) | |
#define TRACE_MSG (1 << 1) | |
#define PIC_MSG (1 << 2) | |
#define TC_MSG (1 << 3) | |
#define RTC_MSG (1 << 4) | |
#define MATH_MSG (1 << 5) | |
#define UART_MSG (1 << 6) | |
#define IRQ_MSG (1 << 7) | |
#define SS1_MAX_TIMERS 3 | |
typedef struct { | |
PNP_INFO pnp; /* Plug and Play */ | |
} SS1_INFO; | |
static SS1_INFO ss1_info_data = { { 0x0, 0, 0x50, 16 } }; | |
extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); | |
extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); | |
extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, | |
int32 (*routine)(const int32, const int32, const int32), uint8 unmap); | |
extern uint32 PCX; | |
static t_stat ss1_reset(DEVICE *ss1_dev); | |
static t_stat ss1_svc (UNIT *uptr); | |
static uint8 SS1_Read(const uint32 Addr); | |
static uint8 SS1_Write(const uint32 Addr, uint8 cData); | |
static int32 ss1dev(const int32 port, const int32 io, const int32 data); | |
void raise_ss1_interrupt(uint8 isr_index); | |
/* SS1 Interrupt Controller notes: | |
* | |
* Msster 8259: | |
* IRQ0 = VI0 | |
* IRQ1 = VI1 - DISK3 Interrupt | |
* IRQ2 = VI2 - IF3 Rx Interrupt | |
* IRQ3 = VI3 - IF3 Tx Interrupt | |
* IRQ4 = VI4 - DISK1A | |
* IRQ5 = VI5 - ? | |
* IRQ6 = VI6 | |
* <Cascade> | |
* | |
* Slave 8259: | |
* IRQ0 = VI7 0x48 | |
* IRQ1 = Timer0 0x49 | |
* IRQ2 = Timer1 0x4A | |
* IRQ3 = Timer2 0x4B | |
* IRQ4 = 9511 SVRQ 0x4C | |
* IRQ5 = 9511 END 0x4D | |
* IRQ6 = 2651 TxRDY 0x4E | |
* IRQ7 = 2651 RxRDY 0x4F | |
*/ | |
#define MASTER_PIC 0 | |
#define SLAVE_PIC 1 | |
#define VI0_IRQ_OFFSET 0 | |
#define VI1_IRQ_OFFSET 1 | |
#define VI2_IRQ_OFFSET 2 | |
#define VI3_IRQ_OFFSET 3 | |
#define VI4_IRQ_OFFSET 4 | |
#define VI5_IRQ_OFFSET 5 | |
#define VI6_IRQ_OFFSET 6 | |
#define VI7_IRQ_OFFSET 0 | |
#define TC0_IRQ_OFFSET 1 | |
#define TC1_IRQ_OFFSET 2 | |
#define TC2_IRQ_OFFSET 3 | |
#define MSVRQ_IRQ_OFFSET 4 | |
#define MEND_IRQ_OFFSET 5 | |
#define TXRDY_IRQ_OFFSET 6 | |
#define RXRDY_IRQ_OFFSET 7 | |
typedef struct { | |
uint8 config_cnt; | |
uint8 ICW[5]; | |
uint8 IMR; /* OCW1 = IMR */ | |
uint8 OCW2; | |
uint8 OCW3; | |
uint8 IRR; | |
uint8 ISR; | |
} I8259_REGS; | |
I8259_REGS ss1_pic[2]; | |
/* SS1 Timer notes: | |
* | |
* T0, T1, T2 inputs connected to 2MHz clock on SS1 | |
* T0 IRQ connected to Slave IRQ 1 | |
* T1 IRQ connected to Slave IRQ 2 | |
* T2 IRQ connected to Slave IRQ 3 | |
*/ | |
typedef struct { | |
uint16 count[3]; /* Current counter value for each timer. */ | |
uint8 mode[3]; /* Current mode of each timer. */ | |
uint8 bcd[3]; | |
uint8 rl[3]; | |
uint8 CTL; | |
} I8253_REGS; | |
I8253_REGS ss1_tc[1]; | |
#define I8253_CTL_SC_MASK 0xC0 | |
#define I8253_CTL_RL_MASK 0x30 | |
#define I8253_CTL_MODE_MASK 0x0E | |
#define I8253_CTL_BCD 0x01 | |
typedef struct { | |
uint8 digit_sel; | |
uint8 flags; | |
} RTC_REGS; | |
RTC_REGS ss1_rtc[1]; | |
static UNIT ss1_unit[] = { | |
{ UDATA (&ss1_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) }, | |
{ UDATA (&ss1_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) }, | |
{ UDATA (&ss1_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) }, | |
{ UDATA (&ss1_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) } | |
}; | |
static REG ss1_reg[] = { | |
{ HRDATA (MPIC_IMR, ss1_pic[MASTER_PIC].IMR, 8), }, | |
{ HRDATA (MPIC_IRR, ss1_pic[MASTER_PIC].IRR, 8), }, | |
{ HRDATA (MPIC_ISR, ss1_pic[MASTER_PIC].ISR, 8), }, | |
{ HRDATA (MPIC_OCW2, ss1_pic[MASTER_PIC].OCW2, 8), }, | |
{ HRDATA (MPIC_OCW3, ss1_pic[MASTER_PIC].OCW3, 8), }, | |
{ HRDATA (SPIC_IMR, ss1_pic[SLAVE_PIC].IMR, 8), }, | |
{ HRDATA (SPIC_IRR, ss1_pic[SLAVE_PIC].IRR, 8), }, | |
{ HRDATA (SPIC_ISR, ss1_pic[SLAVE_PIC].ISR, 8), }, | |
{ HRDATA (SPIC_OCW2, ss1_pic[SLAVE_PIC].OCW2, 8), }, | |
{ HRDATA (SPIC_OCW3, ss1_pic[SLAVE_PIC].OCW3, 8), }, | |
{ HRDATA (T0_MODE, ss1_tc[0].mode, 3), }, | |
{ HRDATA (T0_COUNT, ss1_tc[0].count, 16), }, | |
{ HRDATA (T1_MODE, ss1_tc[1].mode, 3), }, | |
{ HRDATA (T1_COUNT, ss1_tc[1].count, 16), }, | |
{ HRDATA (T2_MODE, ss1_tc[2].mode, 3), }, | |
{ HRDATA (T2_COUNT, ss1_tc[2].count, 16), }, | |
{ HRDATA (RTC_DIGIT, ss1_rtc[0].digit_sel, 4), }, | |
{ HRDATA (RTC_FLAGS, ss1_rtc[0].flags, 4), }, | |
{ NULL } | |
}; | |
static MTAB ss1_mod[] = { | |
{ MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, | |
{ 0 } | |
}; | |
/* Debug Flags */ | |
static DEBTAB ss1_dt[] = { | |
{ "ERROR", ERROR_MSG }, | |
{ "TRACE", TRACE_MSG }, | |
{ "PIC", PIC_MSG }, | |
{ "TC", TC_MSG }, | |
{ "RTC", RTC_MSG }, | |
{ "MATH", MATH_MSG }, | |
{ "UART", UART_MSG }, | |
{ "IRQ", IRQ_MSG }, | |
{ NULL, 0 } | |
}; | |
DEVICE ss1_dev = { | |
"SS1", ss1_unit, ss1_reg, ss1_mod, | |
SS1_MAX_TIMERS, 10, 31, 1, SS1_MAX_TIMERS, SS1_MAX_TIMERS, | |
NULL, NULL, &ss1_reset, | |
NULL, NULL, NULL, | |
&ss1_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, | |
ss1_dt, NULL, "Compupro System Support 1 SS1" | |
}; | |
/* Reset routine */ | |
static t_stat ss1_reset(DEVICE *dptr) | |
{ | |
PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; | |
if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ | |
sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &ss1dev, TRUE); | |
} else { | |
/* Connect SS1 at base address */ | |
if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &ss1dev, FALSE) != 0) { | |
printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); | |
return SCPE_ARG; | |
} else { | |
DBG_PRINT(("SS1: Mapped I/O resource at 0x%04x, len=%d\n", pnp->io_base, pnp->io_size)); | |
ss1_unit[0].u4 = 0; | |
ss1_unit[1].u4 = 1; | |
ss1_unit[2].u4 = 2; | |
ss1_unit[3].u4 = 3; | |
ss1_pic[MASTER_PIC].IMR = 0xFF; | |
ss1_pic[SLAVE_PIC].IMR = 0xFF; | |
} | |
} | |
return SCPE_OK; | |
} | |
static int32 ss1dev(const int32 port, const int32 io, const int32 data) | |
{ | |
DBG_PRINT(("SS1: IO %s, Port %02x\n", io ? "WR" : "RD", port)); | |
if(io) { | |
SS1_Write(port, data); | |
return 0; | |
} else { | |
return(SS1_Read(port)); | |
} | |
} | |
#define SS1_M8259_L 0x00 | |
#define SS1_M8259_H 0x01 | |
#define SS1_S8259_L 0x02 | |
#define SS1_S8259_H 0x03 | |
#define SS1_8253_TC0 0x04 | |
#define SS1_8253_TC1 0x05 | |
#define SS1_8253_TC2 0x06 | |
#define SS1_8253_CTL 0x07 | |
#define SS1_9511A_DATA 0x08 | |
#define SS1_9511A_CMD 0x09 | |
#define SS1_RTC_CMD 0x0A | |
#define SS1_RTC_DATA 0x0B | |
#define SS1_UART_DATA 0x0C | |
#define SS1_UART_STAT 0x0D | |
#define SS1_UART_MODE 0x0E | |
#define SS1_UART_CMD 0x0F | |
extern int32 sio0d(const int32 port, const int32 io, const int32 data); | |
extern int32 sio0s(const int32 port, const int32 io, const int32 data); | |
static struct tm currentTime; | |
static int32 toBCD(const int32 x); | |
static uint8 SS1_Read(const uint32 Addr) | |
{ | |
uint8 cData = 0x00; | |
uint8 sel_pic = MASTER_PIC; | |
uint8 sel_tc = 0; | |
time_t now; | |
switch(Addr & 0x0F) { | |
case SS1_S8259_L: | |
sel_pic = SLAVE_PIC; | |
case SS1_M8259_L: | |
if((ss1_pic[sel_pic].OCW3 & 0x03) == 0x03) { | |
cData = ss1_pic[sel_pic].ISR; | |
sim_debug(PIC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: %s PIC ISR=0x%02x.\n", PCX, (sel_pic ? "Slave " : "Master"), cData); | |
} else if((ss1_pic[sel_pic].OCW3 & 0x03) == 0x02) { | |
cData = ss1_pic[sel_pic].IRR; | |
sim_debug(PIC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: %s PIC IRR=0x%02x.\n", PCX, (sel_pic ? "Slave " : "Master"), cData); | |
} else { | |
cData = 0xFF; | |
} | |
break; | |
case SS1_S8259_H: | |
sel_pic = SLAVE_PIC; | |
case SS1_M8259_H: | |
cData = ss1_pic[sel_pic].IMR; | |
sim_debug(PIC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: %s PIC IMR=0x%02x.\n", PCX, (sel_pic ? "Slave " : "Master"), cData); | |
ss1_pic[sel_pic].IMR = cData; | |
break; | |
case SS1_8253_CTL: | |
cData = ss1_tc[0].CTL; | |
sim_debug(TC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: TC CTL=0x%02x.\n", PCX, cData); | |
break; | |
case SS1_8253_TC2: | |
sel_tc++; | |
case SS1_8253_TC1: | |
sel_tc++; | |
case SS1_8253_TC0: | |
sim_debug(TC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: TC [%d]=0x%02x.\n", PCX, sel_tc, cData); | |
break; | |
case SS1_9511A_DATA: | |
case SS1_9511A_CMD: | |
sim_debug(MATH_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: Math Coprocessor not Implemented.\n", PCX); | |
break; | |
case SS1_RTC_CMD: | |
cData = 0xFF; | |
sim_debug(RTC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: RTC Cmd=0x%02x.\n", PCX, cData); | |
break; | |
case SS1_RTC_DATA: | |
time(&now); | |
currentTime = *localtime(&now); | |
switch(ss1_rtc[0].digit_sel) { | |
case 0: | |
cData = toBCD(currentTime.tm_sec) & 0xF; | |
break; | |
case 1: | |
cData = (toBCD(currentTime.tm_sec) >> 4) & 0xF; | |
break; | |
case 2: | |
cData = toBCD(currentTime.tm_min) & 0xF; | |
break; | |
case 3: | |
cData = (toBCD(currentTime.tm_min) >> 4) & 0xF; | |
break; | |
case 4: | |
cData = toBCD(currentTime.tm_hour) & 0xF; | |
break; | |
case 5: | |
cData = (toBCD(currentTime.tm_hour) >> 4) & 0x3; | |
cData |= 0x08; /* Set to 24-hour format */ | |
break; | |
case 6: | |
cData = toBCD(currentTime.tm_wday) & 0xF; | |
break; | |
case 7: | |
cData = toBCD(currentTime.tm_mday) & 0xF; | |
break; | |
case 8: | |
cData = (toBCD(currentTime.tm_mday) >> 4) & 0xF; | |
break; | |
case 9: | |
cData = toBCD(currentTime.tm_mon+1) & 0xF; | |
break; | |
case 10: | |
cData = (toBCD(currentTime.tm_mon+1) >> 4) & 0xF; | |
break; | |
case 11: | |
cData = toBCD(currentTime.tm_year-22) & 0xF; | |
break; | |
case 12: | |
cData = (toBCD(currentTime.tm_year-22) >> 4) & 0xF; | |
break; | |
default: | |
cData = 0; | |
break; | |
} | |
sim_debug(RTC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: RTC Data[%x]=0x%02x.\n", PCX, ss1_rtc[0].digit_sel, cData); | |
break; | |
case SS1_UART_DATA: | |
cData = sio0d(Addr, 0, 0); | |
sim_debug(UART_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: UART Data=0x%02x.\n", PCX, cData); | |
break; | |
case SS1_UART_STAT: | |
cData = sio0s(Addr, 0, 0); | |
sim_debug(UART_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: UART Stat=0x%02x.\n", PCX, cData); | |
break; | |
case SS1_UART_MODE: | |
case SS1_UART_CMD: | |
sim_debug(UART_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" RD: UART not Implemented.\n", PCX); | |
break; | |
} | |
return (cData); | |
} | |
uint16 newcount = 0; | |
uint8 bc; | |
static void generate_ss1_interrupt(void); | |
static uint8 SS1_Write(const uint32 Addr, uint8 cData) | |
{ | |
uint8 sel_pic = MASTER_PIC; | |
uint8 sel_tc = 0; | |
uint8 sel_timer = 0; | |
switch(Addr & 0x0F) { | |
case SS1_S8259_L: | |
sel_pic = SLAVE_PIC; | |
case SS1_M8259_L: | |
if(cData & 0x10) { | |
sim_debug(PIC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: %s PIC ICW1=0x%02x.\n", PCX, (sel_pic ? "Slave " : "Master"), cData); | |
ss1_pic[sel_pic].ICW[1] = cData; | |
ss1_pic[sel_pic].config_cnt=1; | |
} else { | |
if(cData & 0x08) { /* OCW3 */ | |
sim_debug(PIC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: %s PIC OCW3=0x%02x.\n", PCX, (sel_pic ? "Slave " : "Master"), cData); | |
ss1_pic[sel_pic].OCW3 = cData; | |
} else { /* OCW2 */ | |
sim_debug(PIC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: %s PIC OCW2=0x%02x.\n", PCX, (sel_pic ? "Slave " : "Master"), cData); | |
ss1_pic[sel_pic].OCW2 = cData; | |
} | |
} | |
break; | |
case SS1_S8259_H: | |
sel_pic = SLAVE_PIC; | |
case SS1_M8259_H: | |
if(ss1_pic[sel_pic].config_cnt == 0) { | |
sim_debug(PIC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT " WR: %s PIC IMR=0x%02x.\n", PCX, (sel_pic ? "Slave " : "Master"), cData); | |
ss1_pic[sel_pic].IMR = cData; | |
generate_ss1_interrupt(); | |
} else { | |
ss1_pic[sel_pic].config_cnt++; | |
sim_debug(PIC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT " WR: %s PIC ICW%d=0x%02x.\n", PCX, (sel_pic ? "Slave " : "Master"), ss1_pic[sel_pic].config_cnt, cData); | |
ss1_pic[sel_pic].ICW[ss1_pic[sel_pic].config_cnt] = cData; | |
ss1_unit[0].u3 = ss1_pic[SLAVE_PIC].ICW[2]+TC0_IRQ_OFFSET; | |
ss1_unit[1].u3 = ss1_pic[SLAVE_PIC].ICW[2]+TC1_IRQ_OFFSET; | |
ss1_unit[2].u3 = ss1_pic[SLAVE_PIC].ICW[2]+TC2_IRQ_OFFSET; | |
if(ss1_pic[sel_pic].config_cnt == 4) { | |
ss1_pic[sel_pic].config_cnt = 0; | |
} | |
} | |
break; | |
case SS1_8253_CTL: | |
ss1_tc[0].CTL = cData; | |
sel_timer = (ss1_tc[0].CTL & I8253_CTL_SC_MASK) >> 6; | |
sim_debug(TC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: TC CTL=0x%02x.\n", | |
PCX, ss1_tc[0].CTL); | |
if(ss1_tc[0].CTL & I8253_CTL_BCD) { | |
sim_debug(ERROR_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" Timer %d: BCD Mode not supported: TC CTL=0x%02x.\n", | |
PCX, sel_timer, ss1_tc[0].CTL); | |
} | |
ss1_tc[0].bcd[sel_timer] = (ss1_tc[0].CTL & I8253_CTL_BCD); | |
ss1_tc[0].mode[sel_timer] = (ss1_tc[0].CTL & I8253_CTL_MODE_MASK) >> 1; | |
ss1_tc[0].rl[sel_timer] = (ss1_tc[0].CTL & I8253_CTL_RL_MASK) >> 4; | |
sim_debug(TRACE_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" Timer %d: Mode: %d, RL=%d, %s.\n", | |
PCX, sel_timer, ss1_tc[0].mode[sel_timer], | |
ss1_tc[0].rl[sel_timer], | |
ss1_tc[0].bcd[sel_timer] ? "BCD" : "Binary"); | |
newcount = 0; | |
bc=0; | |
break; | |
case SS1_8253_TC2: | |
sel_tc++; | |
case SS1_8253_TC1: | |
sel_tc++; | |
case SS1_8253_TC0: | |
if(ss1_tc[0].rl[sel_timer] == 3) { | |
if(bc==0) { | |
newcount = cData; | |
} | |
if(bc==1) { | |
newcount |= (cData << 8); | |
sim_activate(&ss1_unit[sel_tc], newcount); | |
} | |
bc++; | |
} | |
if(ss1_tc[0].rl[sel_timer] == 2) { | |
newcount = (cData << 8); | |
sim_activate(&ss1_unit[sel_tc], newcount); | |
} | |
sim_debug(TC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: TC [%d]=0x%02x.\n", PCX, sel_tc, cData); | |
if(sel_tc == 0) { | |
} | |
break; | |
case SS1_9511A_DATA: | |
case SS1_9511A_CMD: | |
sim_debug(TRACE_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: Math Coprocessor not Implemented.\n", PCX); | |
break; | |
case SS1_RTC_CMD: | |
ss1_rtc[0].digit_sel = cData & 0x0F; | |
ss1_rtc[0].flags = (cData >> 4) & 0x0F; | |
sim_debug(RTC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: RTC Cmd=0x%02x (%s%s%s SEL=%x)\n", | |
PCX, cData, | |
ss1_rtc[0].flags & 0x4 ? "HOLD " :"", | |
ss1_rtc[0].flags & 0x2 ? "WR" :"", | |
ss1_rtc[0].flags & 0x1 ? "RD" :"", | |
ss1_rtc[0].digit_sel); | |
break; | |
case SS1_RTC_DATA: | |
sim_debug(RTC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: RTC Data=0x%02x\n", PCX, cData); | |
break; | |
case SS1_UART_DATA: | |
sim_debug(UART_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: UART Data=0x%02x.\n", PCX, cData); | |
sio0d(Addr, 1, cData); | |
break; | |
case SS1_UART_STAT: | |
sim_debug(UART_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: UART Stat=0x%02x.\n", PCX, cData); | |
sio0s(Addr, 1, cData); | |
break; | |
case SS1_UART_MODE: | |
case SS1_UART_CMD: | |
sim_debug(TRACE_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT | |
" WR: UART not Implemented.\n", PCX); | |
break; | |
} | |
return(0); | |
} | |
void raise_ss1_interrupt(uint8 isr_index) | |
{ | |
uint8 irq_bit; | |
if(isr_index < 7) { /* VI0-6 on master PIC */ | |
irq_bit = (1 << isr_index); | |
ss1_pic[MASTER_PIC].ISR |= irq_bit; | |
generate_ss1_interrupt(); | |
} else { /* VI7 is on slave PIC */ | |
ss1_pic[SLAVE_PIC].ISR |= 1; | |
generate_ss1_interrupt(); | |
} | |
} | |
extern void cpu_raise_interrupt(uint32 irq); | |
static void generate_ss1_interrupt(void) | |
{ | |
uint8 irq, irq_pend, irq_index = 0, irq_bit = 0; | |
uint8 pic; | |
for(pic=MASTER_PIC;pic<=SLAVE_PIC;pic++) { | |
irq_pend = (~ss1_pic[pic].IMR) & ss1_pic[pic].ISR; | |
while(irq_pend) { | |
irq_bit = irq_pend & 1; | |
if(irq_bit) { | |
ss1_pic[pic].IRR |= (irq_bit << irq_index); | |
irq = ss1_pic[pic].ICW[2]+irq_index; | |
sim_debug(IRQ_MSG, &ss1_dev, "Handling interrupt on %s PIC: IMR=0x%02x, ISR=0x%02x, IRR=0x%02x, index=%d\n", pic ? "SLAVE" : "MASTER", ss1_pic[pic].IMR, ss1_pic[pic].ISR, ss1_pic[pic].IRR, irq_index); | |
cpu_raise_interrupt(irq); | |
ss1_pic[pic].IRR &= ~(irq_bit << irq_index); | |
ss1_pic[pic].ISR &= ~(irq_bit << irq_index); | |
if(irq_pend & 0x7E) { | |
/* sim_debug(IRQ_MSG, &ss1_dev, "Requeue interrupt on %s PIC: IMR=0x%02x, ISR=0x%02x, IRR=0x%02x, index=%d\n", pic ? "SLAVE" : "MASTER", ss1_pic[pic].IMR, ss1_pic[pic].ISR, ss1_pic[pic].IRR, irq_index); | |
*/ | |
sim_activate(&ss1_unit[3], 1000); /* requeue, because more interrupts are pending. */ | |
} | |
break; | |
} else { | |
irq_index++; | |
irq_pend = irq_pend >> 1; | |
} | |
} | |
} | |
} | |
/* Unit service routine */ | |
/* Unit 0-2 = Timer0-2, Unit3=ISR queue */ | |
static t_stat ss1_svc (UNIT *uptr) | |
{ | |
uint8 cData; | |
uint8 irq_bit = 0; | |
/* Handle SS1 UART Rx interrupts here. */ | |
cData = sio0s(0x5D, 0, 0); | |
if(cData & 2) { /* && ((ss1_pic[SLAVE_PIC].IMR & 0x80) == 0)) { */ | |
ss1_pic[SLAVE_PIC].ISR |= 0x80; | |
generate_ss1_interrupt(); | |
sim_activate(uptr, 1000); /* requeue, because we still need to handle the timer interrupt. */ | |
} else if((cData & 1) && ((ss1_pic[SLAVE_PIC].IMR & 0x40) == 0)) { | |
sim_debug(IRQ_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT " Calling UART Tx ISR.\n", PCX); | |
ss1_pic[SLAVE_PIC].ISR |= 0x40; | |
generate_ss1_interrupt(); | |
sim_activate(uptr, 1000); /* requeue, because we still need to handle the timer interrupt. */ | |
} else if (uptr->u4 == 0x3) { /* ISR was requeued because additional interrupts were pending. */ | |
generate_ss1_interrupt(); | |
} else { | |
switch(uptr->u4) { | |
case 0: | |
irq_bit = 2; | |
break; | |
case 1: | |
irq_bit = 4; | |
break; | |
case 2: | |
irq_bit = 8; | |
break; | |
} | |
if(ss1_tc[0].mode[uptr->u4] == 0x0) { | |
sim_debug(TC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT " Calling Timer%d ISR.\n", PCX, uptr->u4); | |
ss1_pic[SLAVE_PIC].ISR |= irq_bit; | |
generate_ss1_interrupt(); | |
} | |
if(ss1_tc[0].mode[uptr->u4] == 0x3) { | |
sim_debug(TC_MSG, &ss1_dev, "SS1: " ADDRESS_FORMAT " Calling Timer%d ISR.\n", PCX, uptr->u4); | |
ss1_pic[SLAVE_PIC].ISR |= irq_bit; | |
generate_ss1_interrupt(); | |
sim_debug(TC_MSG, &ss1_dev, "Timer %d, mode %d, reloading\n", uptr->u4, ss1_tc[0].mode[uptr->u4]); | |
sim_activate(uptr, 33280); | |
} | |
} | |
sim_activate(&ss1_unit[3], 1000000); // requeue, because more interrupts are pending. | |
return SCPE_OK; | |
} | |
static int32 toBCD(const int32 x) { | |
return (x / 10) * 16 + (x % 10); | |
} | |