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

#define TRACE_PRINT(level, args)    if(ss1_dev.dctrl & level) { \
                                       printf args;             \
                                    }

/* 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;
                TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " RD: %s PIC ISR=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData));
            } else if((ss1_pic[sel_pic].OCW3 & 0x03) == 0x02) {
                cData = ss1_pic[sel_pic].IRR;
                TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " RD: %s PIC IRR=0x%02x." NLP, 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;
            TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " RD: %s PIC IMR=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData));
            ss1_pic[sel_pic].IMR = cData;
            break;
        case SS1_8253_CTL:
            cData = ss1_tc[0].CTL;
            TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " RD: TC CTL=0x%02x." NLP, PCX, cData));
            break;
        case SS1_8253_TC2:
            sel_tc++;
        case SS1_8253_TC1:
            sel_tc++;
        case SS1_8253_TC0:
            TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " RD: TC [%d]=0x%02x." NLP, PCX, sel_tc, cData));
            break;
        case SS1_9511A_DATA:
        case SS1_9511A_CMD:
            TRACE_PRINT(MATH_MSG, ("SS1: " ADDRESS_FORMAT " RD: Math Coprocessor not Implemented." NLP, PCX));
            break;
        case SS1_RTC_CMD:
            cData = 0xFF;
            TRACE_PRINT(RTC_MSG, ("SS1: " ADDRESS_FORMAT " RD: RTC  Cmd=0x%02x." NLP, 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;
            }
            TRACE_PRINT(RTC_MSG, ("SS1: " ADDRESS_FORMAT " RD: RTC Data[%x]=0x%02x." NLP, PCX, ss1_rtc[0].digit_sel, cData));

            break;
        case SS1_UART_DATA:
            cData = sio0d(Addr, 0, 0);
            TRACE_PRINT(UART_MSG, ("SS1: " ADDRESS_FORMAT " RD: UART Data=0x%02x." NLP, PCX, cData));
            break;
        case SS1_UART_STAT:
            cData = sio0s(Addr, 0, 0);
            TRACE_PRINT(UART_MSG, ("SS1: " ADDRESS_FORMAT " RD: UART Stat=0x%02x." NLP, PCX, cData));
            break;
        case SS1_UART_MODE:
        case SS1_UART_CMD:
            TRACE_PRINT(UART_MSG, ("SS1: " ADDRESS_FORMAT " RD: UART not Implemented." NLP, 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) {
                TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " WR: %s PIC ICW1=0x%02x." NLP, 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 */
                    TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " WR: %s PIC OCW3=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData));
                    ss1_pic[sel_pic].OCW3 = cData;
                } else { /* OCW2 */
                    TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " WR: %s PIC OCW2=0x%02x." NLP, 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) {
                TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " WR: %s PIC IMR=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData));
                ss1_pic[sel_pic].IMR = cData;
                generate_ss1_interrupt();
            } else {
                ss1_pic[sel_pic].config_cnt++;
                TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " WR: %s PIC ICW%d=0x%02x." NLP, 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;
            TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " WR: TC CTL=0x%02x." NLP, PCX, ss1_tc[0].CTL));
            if(ss1_tc[0].CTL & I8253_CTL_BCD) {
                TRACE_PRINT(ERROR_MSG, ("SS1: " ADDRESS_FORMAT " Timer %d: BCD Mode not supported: TC CTL=0x%02x." NLP, 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;
            TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " Timer %d: Mode: %d, RL=%d, %s." NLP, 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);
            }

            TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " WR: TC [%d]=0x%02x." NLP, PCX, sel_tc, cData));
            if(sel_tc == 0) {
            }
            break;
        case SS1_9511A_DATA:
        case SS1_9511A_CMD:
            TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " WR: Math Coprocessor not Implemented." NLP, PCX));
            break;
        case SS1_RTC_CMD:
            ss1_rtc[0].digit_sel = cData & 0x0F;
            ss1_rtc[0].flags = (cData >> 4) & 0x0F;
            TRACE_PRINT(RTC_MSG, ("SS1: " ADDRESS_FORMAT " WR: RTC  Cmd=0x%02x (%s%s%s SEL=%x)" NLP,
                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:
            TRACE_PRINT(RTC_MSG, ("SS1: " ADDRESS_FORMAT " WR: RTC Data=0x%02x" NLP, PCX, cData));
            break;
        case SS1_UART_DATA:
            TRACE_PRINT(UART_MSG, ("SS1: " ADDRESS_FORMAT " WR: UART Data=0x%02x." NLP, PCX, cData));
            sio0d(Addr, 1, cData);
            break;
        case SS1_UART_STAT:
            TRACE_PRINT(UART_MSG, ("SS1: " ADDRESS_FORMAT " WR: UART Stat=0x%02x." NLP, PCX, cData));
            sio0s(Addr, 1, cData);
            break;
        case SS1_UART_MODE:
        case SS1_UART_CMD:
            TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " WR: UART not Implemented." NLP, 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;
                TRACE_PRINT(IRQ_MSG, ("Handling interrupt on %s PIC: IMR=0x%02x, ISR=0x%02x, IRR=0x%02x, index=%d" NLP,
                    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) {
/*              TRACE_PRINT(IRQ_MSG, ("Requeue interrupt on %s PIC: IMR=0x%02x, ISR=0x%02x, IRR=0x%02x, index=%d" NLP,
                    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)) {
        TRACE_PRINT(IRQ_MSG, ("SS1: " ADDRESS_FORMAT " Calling UART Tx ISR." NLP, 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) {
            TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " Calling Timer%d ISR." NLP, PCX, uptr->u4));
            ss1_pic[SLAVE_PIC].ISR |= irq_bit;
            generate_ss1_interrupt();
        }
        if(ss1_tc[0].mode[uptr->u4] == 0x3) {
            TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " Calling Timer%d ISR." NLP, PCX, uptr->u4));
            ss1_pic[SLAVE_PIC].ISR |= irq_bit;
            generate_ss1_interrupt();
            TRACE_PRINT(TC_MSG, ("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);
}

