| /************************************************************************* | |
| * * | |
| * $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); | |
| } | |