These changes facilitate more robust parameter type checking and helps to identify unexpected coding errors. Most simulators can now also be compiled with a C++ compiler without warnings. Additionally, these changes have also been configured to facilitate easier backporting of simulator and device simulation modules to run under the simh v3.9+ SCP framework.
637 lines
21 KiB
C
637 lines
21 KiB
C
/* sage_stddev.c: Standard devices for sage-II system
|
|
|
|
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.
|
|
|
|
04-Oct-09 HV Initial version
|
|
*/
|
|
|
|
#include "sim_defs.h"
|
|
#include "m68k_cpu.h"
|
|
#include "sage_defs.h"
|
|
|
|
/***********************************************************************************
|
|
* 8259-5 interrupt controller
|
|
*
|
|
* IRQ output hardwired to Interrupt Priority Level 1 in the Sage
|
|
* Level 2: from external bus (wired to HDC board, AUX devices)
|
|
* Level 3: from external bus
|
|
* Level 4: IEEE 488 Interrupt U6
|
|
* Level 5: Console Uart U67 Receiver Interrupt
|
|
* Level 6: FDI floppy controller
|
|
* Level 7: nonmaskable RAM parity error (not possible in simh)
|
|
*
|
|
* hardwired inputs:
|
|
* IR0 = Output 2 of U74 real time clock
|
|
* IR1 = Modem Uart U58 Receiver Interrupt
|
|
* IR2 = Console Uart U67 Transmitter Interrupt
|
|
* IR3 = Modem Uart U58 Receiver Interrupt
|
|
* IR4 = Modem Carrier Detect Interrupt U38
|
|
* IR5 = LP Port Acknowledge U39/U38
|
|
* IR6 = Output 0 of U74 real time clock
|
|
* IR7 = Output C2 of U39
|
|
*
|
|
* Notes:
|
|
* INTA- is hardwired to VCC, so vectoring is not possible
|
|
* SP- is hardwired to VCC, so buffered mode is not possible, and device is a master.
|
|
* CAS0-2 lines are open, no need to handle
|
|
* UCSD bios and boot prom do not program the PIC for rotating priorities,
|
|
* so effectively prio is always 7.
|
|
*
|
|
**********************************************************************************/
|
|
extern DEVICE sagepic_dev;
|
|
static t_stat sagepic_reset(DEVICE* dptr);
|
|
static I8259 u73 = { {0,0,U73_ADDR,4,2},
|
|
&sagepic_dev,NULL,NULL,i8259_reset
|
|
};
|
|
|
|
UNIT sagepic_unit = {
|
|
UDATA (NULL, UNIT_IDLE, 0)
|
|
};
|
|
|
|
REG sagepic_reg[] = {
|
|
{ DRDATA(STATE, u73.state, 8) },
|
|
{ HRDATA(IRR, u73.irr, 8) },
|
|
{ HRDATA(IMR, u73.imr, 8) },
|
|
{ HRDATA(ISR, u73.isr, 8) },
|
|
{ HRDATA(ICW1, u73.icw1, 8) },
|
|
{ HRDATA(ICW2, u73.icw2, 8) },
|
|
{ HRDATA(ICW4, u73.icw4, 8) },
|
|
{ HRDATA(OCW2, u73.prio, 3) },
|
|
{ NULL }
|
|
};
|
|
|
|
static MTAB sagepic_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "IO", "IO", &set_iobase, &show_iobase, NULL },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE sagepic_dev = {
|
|
"PIC", &sagepic_unit, sagepic_reg, sagepic_mod,
|
|
1, 16, 32, 2, 16, 16,
|
|
NULL, NULL, &sagepic_reset,
|
|
NULL, NULL, NULL,
|
|
&u73, DEV_DEBUG, 0,
|
|
i8259_dt, NULL, NULL
|
|
};
|
|
|
|
static t_stat sagepic_reset(DEVICE* dptr)
|
|
{
|
|
t_stat rc;
|
|
if ((rc = (dptr->flags & DEV_DIS) ?
|
|
del_iohandler(dptr->ctxt) :
|
|
add_iohandler(&sagepic_unit,dptr->ctxt,i8259_io)) != SCPE_OK) return rc;
|
|
return u73.reset(&u73);
|
|
}
|
|
|
|
t_stat sage_raiseint(int level)
|
|
{
|
|
return i8259_raiseint(&u73,level);
|
|
}
|
|
|
|
/******************************************************************************************************
|
|
* DIP switches at the back panel.
|
|
*
|
|
* In the technical manual, switches are layed out 12345678 left to right,
|
|
* but here seen as two HEX digits 8765 4321, i.e. 0xc0 is bit 8 and bit 7 set on
|
|
*
|
|
* a "d" (down) means switch is off or "0", and a "u" (up) means switch is on or "1"
|
|
*
|
|
* Note that programatically dip switches are port a and b of the onboard 8255 U22
|
|
* which also through port c serves part of the FDC signals
|
|
*
|
|
* group-a:
|
|
* 8 7 6 5 4 3 2 1
|
|
* | | | | | d d d--- 19,2K baud
|
|
* | | | | | d d u--- 9600 baud
|
|
* | | | | | d u d--- 4800 baud
|
|
* | | | | | d u u--- 2400 baud
|
|
* | | | | | u d d--- 1200 baud
|
|
* | | | | | u d u--- 600 baud
|
|
* | | | | | u u d--- 300 baud
|
|
* | | | | | u u u--- reserved
|
|
* | | | | d--------- even parity
|
|
* | | | | u--------- parity disabled
|
|
* | | d d----------- boot to debugger
|
|
* | | d u----------- boot to floppy 0
|
|
* | | u d----------- boot to harddisk 0 partition 0
|
|
* | | u u----------- reserved
|
|
* | d--------------- 96 tpi drive
|
|
* | u--------------- 48 tpi drive
|
|
* x----------------- reserved
|
|
*
|
|
* group-b:
|
|
* 8 7 6 5 4 3 2 1
|
|
* | | | +-+-+-+-+--- device talk and listen address
|
|
* | | u------------- enable talk
|
|
* | | d------------- disable talk
|
|
* | u--------------- enable listen
|
|
* | d--------------- disable listen
|
|
* u----------------- 2 consecutive addresses
|
|
* d----------------- 1 address
|
|
*/
|
|
|
|
#if defined(SAGE_IV)
|
|
uint32 groupa = 0xd7; /* used by cons device, 19k2, no parity, boot floppy 0 */
|
|
uint32 groupb = 0xf8; /* used by ieee device */
|
|
#else
|
|
uint32 groupa = 0xe7; /* used by cons device, 19k2, no parity, boot winchester 0 */
|
|
uint32 groupb = 0xf8; /* used by ieee device */
|
|
#endif
|
|
|
|
static t_stat sagedip_reset(DEVICE* dptr);
|
|
static t_stat set_groupa(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
static t_stat show_groupa(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
static t_stat set_groupb(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
static t_stat show_groupb(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
static t_stat u22_reset(I8255* chip);
|
|
static t_stat u22_calla(I8255* chip,int rw);
|
|
static t_stat u22_callb(I8255* chip,int rw);
|
|
static t_stat u22_callc(I8255* chip,int rw);
|
|
static t_stat u22_ckmode(I8255* chip,uint32 data);
|
|
|
|
extern DEVICE sagedip_dev;
|
|
static I8255 u22 = {
|
|
{ 0,0,U22_ADDR,8,2 },
|
|
&sagedip_dev,i8255_write,i8255_read,u22_reset,u22_calla,u22_callb,u22_callc,u22_ckmode
|
|
};
|
|
uint32* u22_portc = &u22.portc; /* this is used in the FD device as well, but whole 8255 is handled here */
|
|
|
|
UNIT sagedip_unit = {
|
|
UDATA (NULL, UNIT_IDLE, 0)
|
|
};
|
|
|
|
REG sagedip_reg[] = {
|
|
{ HRDATA(PORTA, u22.porta, 8) },
|
|
{ HRDATA(PORTB, u22.portb, 8) },
|
|
{ HRDATA(PORTC, u22.portc, 8) },
|
|
{ HRDATA(CTRL, u22.ctrl, 8) },
|
|
{ NULL }
|
|
};
|
|
|
|
static MTAB sagedip_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "IO", "IO", &set_iobase, &show_iobase, NULL },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "GROUPA", "GROUPA", &set_groupa, &show_groupa, NULL },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "GROUPB", "GROUPB", &set_groupb, &show_groupb, NULL },
|
|
{ 0 }
|
|
};
|
|
|
|
/* Debug Flags */
|
|
DEBTAB sagedip_dt[] = {
|
|
{ "RDA", DBG_PP_RDA },
|
|
{ "RDB", DBG_PP_RDB },
|
|
{ "WRC", DBG_PP_WRC },
|
|
{ "WRMODE", DBG_PP_MODE },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
DEVICE sagedip_dev = {
|
|
"DIP", &sagedip_unit, sagedip_reg, sagedip_mod,
|
|
1, 16, 32, 2, 16, 16,
|
|
NULL, NULL, &sagedip_reset,
|
|
NULL, NULL, NULL,
|
|
&u22, DEV_DEBUG, 0,
|
|
sagedip_dt, NULL, NULL
|
|
};
|
|
|
|
static t_stat sagedip_reset(DEVICE* dptr)
|
|
{
|
|
t_stat rc;
|
|
|
|
if ((rc = (dptr->flags & DEV_DIS) ? /* Disconnect I/O Ports */
|
|
del_iohandler(dptr->ctxt) :
|
|
add_iohandler(&sagedip_unit,dptr->ctxt,i8255_io)) != SCPE_OK) return rc;
|
|
|
|
/* clear 8255 ctrl register */
|
|
return u22.reset(&u22);
|
|
}
|
|
|
|
static t_stat set_gr(const char* cptr, uint32* sw)
|
|
{
|
|
int i;
|
|
char c;
|
|
|
|
if (!cptr) return SCPE_ARG;
|
|
|
|
*sw = 0;
|
|
for (i=0; *cptr && i<8; i++) {
|
|
c = *cptr++;
|
|
*sw <<= 1;
|
|
if (c=='1') *sw |= 1;
|
|
else if (c=='0') continue;
|
|
else if (c==0) break;
|
|
else return SCPE_ARG;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat set_groupa(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
return set_gr(cptr,&groupa);
|
|
}
|
|
|
|
static t_stat set_groupb(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
return set_gr(cptr,&groupb);
|
|
}
|
|
|
|
static t_stat show_gr(FILE* st, const char* prefix, uint32 gr)
|
|
{
|
|
int i;
|
|
fputs(prefix, st);
|
|
for (i = 0x80; i > 0; i = i >> 1)
|
|
fprintf(st,"%c", gr&i ? '1' : '0');
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat show_groupa(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
return show_gr(st, "GROUPA=", groupa);
|
|
}
|
|
|
|
static t_stat show_groupb(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
return show_gr(st, "GROUPB=", groupb);
|
|
}
|
|
|
|
static t_stat u22_reset(I8255* chip)
|
|
{
|
|
chip->ctrl = 0;
|
|
chip->portc = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
extern I8272 u21;
|
|
|
|
static t_stat u22_calla(I8255* chip,int rw)
|
|
{
|
|
if (rw==0) {
|
|
chip->porta = groupa & 0xff;
|
|
TRACE_PRINT1(DBG_PP_RDA,"WR PortA: 0x%x",groupa);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat u22_callb(I8255* chip,int rw)
|
|
{
|
|
if (rw==0) {
|
|
chip->portb = groupb & 0xff;
|
|
TRACE_PRINT1(DBG_PP_RDA,"WR PortB: 0x%x",groupb);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* callback handler for FDC bits */
|
|
static t_stat u22_callc(I8255* chip,int rw)
|
|
{
|
|
/* bit0: TC+ positive enforce that internal data counter of FDC is reset
|
|
* bit1: RDY+ positive enable the FDC
|
|
* bit2: FDIE+ positive enable FDC interrupt (handled directly by reading portc in sage_fd.c)
|
|
* bit3: SL0- negative select of drive 0
|
|
* bit4: SL1- negative select of drive 1
|
|
* bit5: MOT- negative switch on drive motor (ignored)
|
|
* bit6: PCRMP- negative precompensation (ignored)
|
|
* bit7: FRES+ positive FDC reset
|
|
*/
|
|
|
|
if (I8255_ISSET(portc,U22C_TC)) { /* TC+ */
|
|
i8272_finish(&u21); /* terminate a read/write in progress */
|
|
}
|
|
if (I8255_ISCLR(portc,U22C_RDY)) { /* RDY+ */
|
|
i8272_abortio(&u21); /* abort current op */
|
|
}
|
|
if (I8255_ISCLR(portc,U22C_SL0)) { /* SL0- */
|
|
u21.fdc_curdrv = 0;
|
|
} else if (I8255_ISCLR(portc,U22C_SL1)) { /* SL1- */
|
|
u21.fdc_curdrv = 1;
|
|
} else if (I8255_ISSET(portc,U22C_SL0|U22C_SL1)) { /* deselect drives */
|
|
u21.fdc_curdrv = 0;
|
|
}
|
|
if (I8255_ISSET(portc,U22C_FRES)) { /* FRES+ */
|
|
i8272_reset(&u21);
|
|
}
|
|
TRACE_PRINT(DBG_PP_WRC,(sim_deb,"PORTC Flags: %s%s%s%s%s%s%s%s",
|
|
I8255_ISSET(portc,U22C_TC)?"TC ":"",
|
|
I8255_ISSET(portc,U22C_RDY)?"RDY ":"",
|
|
I8255_ISSET(portc,U22C_FDIE)?"FDIE ":"",
|
|
I8255_ISSET(portc,U22C_SL0)?"":"SL0 ",
|
|
I8255_ISSET(portc,U22C_SL1)?"":"SL1 ",
|
|
I8255_ISSET(portc,U22C_MOT)?"":"MOT ",
|
|
I8255_ISSET(portc,U22C_PCRMP)?"":"PCRMP ",
|
|
I8255_ISSET(portc,U22C_FRES)?"FRES ":""));
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat u22_ckmode(I8255* chip, uint32 data)
|
|
{
|
|
/* hardwired:
|
|
* d7=1 -- mode set flag
|
|
* d6=0 -+ group a mode 0: basic I/O
|
|
* d5=0 -+
|
|
* d4=1 -- porta = input
|
|
* d3=0 -- portc upper = output
|
|
* d2=0 -- group b mode 0: basic I/O
|
|
* d1=1 -- portb = input
|
|
* d0=0 -- portc lower = output
|
|
*/
|
|
TRACE_PRINT1(DBG_PP_MODE,"WR Mode: 0x%x",data);
|
|
if (data != 0x92) {
|
|
printf("u22_ckmode: unsupported ctrl=0x%02x\n",data);
|
|
return STOP_IMPL;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/***********************************************************************************
|
|
* Two 8553 timers U75 (TIMER1) and U74 (TIMER2)
|
|
* Each contain three 8/16 bit timers
|
|
* In the sage hardwired in the following way:
|
|
*
|
|
* +---------+
|
|
* 615kHz--+->|Timer1 C1|---> Baud ser0
|
|
* | +---------+
|
|
* +->|Timer1 C2|---> Baud ser1
|
|
* +---------+
|
|
* +---------+ +---------+
|
|
* 64kHz---+->|Timer1 C0|--->|Timer2 C0|--> PIC IR6
|
|
* | |div 64000| |mode0 |
|
|
* | +---------+ +---------+
|
|
* | +---------+ +---------+
|
|
* +->|Timer2 C1|--->|Timer2 C2|--> PIC IR0
|
|
* | | | |
|
|
* +---------+ +---------+
|
|
*
|
|
* NOT UNITS: Timer1 C1 and C2 are programmed in mode 2 as clock dividers for the USARTs
|
|
* In this emulation we allow programming them, but since they don't produce interrupts,
|
|
* their work is ignored.
|
|
*
|
|
* Timer1 C0 and timer2 C0 form a clock divider which produces an interrupt at PIC level 6
|
|
* Likewise, timer2 C1 and timer2 C2 form a clock divider which produces an interrupt at PIC level 0
|
|
*
|
|
* Typically, the first one in cascade is programmed in mode 2, the second one in mode 0.
|
|
* Timer1 C0 is explicitly programmed as a divider by 64k, so that it feeds timer2 C0
|
|
* with a 1Hz clock.
|
|
*
|
|
* The way the timers are hardwired makes certain mode settings impossible: all GATE
|
|
* inputs are set to VCC, so MODE1 and MODE5 are impossible, and MODE4 becomes a
|
|
* variant of MODE0. MODE3 is used by the baudrate generators. The timers may run in
|
|
* 8 bit mode, but analysis of existing BIOS code (BOOT PROM and UCSD BIOS) uncovered
|
|
* they are used in 16 bit mode only.
|
|
* So, this implementation only contains the most likely usages, and the other ones have
|
|
* to be added when there is a necessity.
|
|
*
|
|
* Notes on actual implementation:
|
|
* Since we know the input clocks, we have just to take care about the division factors
|
|
* stored in T1C0 and T2C1. Whenever one of these timers are read out, the actual count
|
|
* has to be calculated on the fly. The actual cnt registers only hold the count factors
|
|
* programmed, but are never counted down, as, in the case of the 64kHz clock this would
|
|
* mean to trigger sim_* events 64000 times a second.
|
|
*
|
|
***********************************************************************************/
|
|
|
|
/************************************************************************************
|
|
* timer 1
|
|
***********************************************************************************/
|
|
static t_stat sagetimer1_reset(DEVICE* dptr);
|
|
static t_stat timer1_svc(UNIT* uptr);
|
|
static t_stat u75_ckmode(I8253* chip, uint32 data);
|
|
static t_stat u75_call0(I8253* chip,int addr, uint32* value);
|
|
|
|
static t_stat sagetimer2_reset(DEVICE* dptr);
|
|
static t_stat timer2_svc(UNIT* uptr);
|
|
static t_stat u74_ckmode(I8253* chip, uint32 data);
|
|
static t_stat u74_call1(I8253* chip,int addr, uint32* value);
|
|
|
|
extern DEVICE sagetimer1_dev;
|
|
extern DEVICE sagetimer2_dev;
|
|
|
|
/* forward timer 2 */
|
|
UNIT sagetimer2_unit = {
|
|
UDATA (&timer2_svc, UNIT_IDLE, 0)
|
|
};
|
|
|
|
static I8253 u74 = { {0,0,U74_ADDR,8,2},
|
|
&sagetimer2_dev,&sagetimer2_unit,i8253_reset,u74_ckmode,
|
|
{ { 0, }, { u74_call1, }, { 0, } }
|
|
};
|
|
|
|
/* timer 1 */
|
|
UNIT sagetimer1_unit = {
|
|
UDATA (&timer1_svc, UNIT_IDLE, 1)
|
|
};
|
|
|
|
static I8253 u75 = { {0,0,U75_ADDR,8,2},
|
|
&sagetimer1_dev,&sagetimer1_unit,i8253_reset,u75_ckmode,
|
|
{ { u75_call0, }, { 0, }, { 0, } }
|
|
};
|
|
|
|
REG sagetimer1_reg[] = {
|
|
{ HRDATA(INIT, u75.init, 8), REG_HRO },
|
|
{ HRDATA(STATE0,u75.cntr[0].state, 8),REG_HRO },
|
|
{ HRDATA(STATE1,u75.cntr[1].state, 8),REG_HRO },
|
|
{ HRDATA(STATE2,u75.cntr[2].state, 8),REG_HRO },
|
|
{ HRDATA(MODE0, u75.cntr[0].mode, 8) },
|
|
{ HRDATA(MODE1, u75.cntr[1].mode, 8) },
|
|
{ HRDATA(MODE2, u75.cntr[2].mode, 8) },
|
|
{ HRDATA(CNT0, u75.cntr[0].count, 16) },
|
|
{ HRDATA(CNT1, u75.cntr[1].count, 16) },
|
|
{ HRDATA(CNT2, u75.cntr[2].count, 16) },
|
|
{ HRDATA(LATCH0,u75.cntr[0].latch, 16) },
|
|
{ HRDATA(LATCH1,u75.cntr[1].latch, 16) },
|
|
{ HRDATA(LATCH2,u75.cntr[2].latch, 16) },
|
|
{ HRDATA(DIV0, u75.cntr[0].divider, 16),REG_HRO },
|
|
{ HRDATA(DIV1, u75.cntr[1].divider, 16),REG_HRO },
|
|
{ HRDATA(DIV2, u75.cntr[2].divider, 16),REG_HRO },
|
|
{ NULL }
|
|
};
|
|
|
|
static MTAB sagetimer1_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "IO", "IO", &set_iobase, &show_iobase, NULL },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE sagetimer1_dev = {
|
|
"TIMER1", &sagetimer1_unit, sagetimer1_reg, sagetimer1_mod,
|
|
1, 16, 32, 2, 16, 16,
|
|
NULL, NULL, &sagetimer1_reset,
|
|
NULL, NULL, NULL,
|
|
&u75, DEV_DEBUG, 0,
|
|
i8253_dt, NULL, NULL
|
|
};
|
|
|
|
static t_stat sagetimer1_reset(DEVICE* dptr)
|
|
{
|
|
t_stat rc;
|
|
|
|
if (dptr->flags & DEV_DIS)
|
|
rc = del_iohandler(dptr->ctxt);
|
|
else
|
|
rc = add_iohandler(&sagetimer1_unit,dptr->ctxt,i8253_io);
|
|
if (rc != SCPE_OK)
|
|
return rc;
|
|
return u75.reset(&u75);
|
|
}
|
|
|
|
static t_stat timer1_svc(UNIT* uptr)
|
|
{
|
|
int32 wait;
|
|
I8253CNTR* t1c0 = &u75.cntr[0];
|
|
I8253CNTR* t2c0 = &u74.cntr[0];
|
|
|
|
// fprintf(sim_deb,"TIMER1: timer1_svc called T1C0=%d T2C0=%d\n",t1c0->count,t2c0->count);
|
|
/* we call this service 64000 times a second to decrement counter T1C0.
|
|
* When T1C0 reaches 0, it will decrement T2C0 */
|
|
t1c0->count--;
|
|
if (t1c0->count <= 0) {
|
|
/* reload from divider */
|
|
t1c0->count = t1c0->divider;
|
|
/* decrement T2C0 counter and raise interrupt 6 if counter is zero */
|
|
if (t2c0->count == 0) {
|
|
sage_raiseint(TIMER2C0_PICINT);
|
|
// printf("timer1 heartbeat\n");
|
|
t2c0->count = 65536;
|
|
}
|
|
t2c0->count--;
|
|
}
|
|
|
|
/* adjust timing */
|
|
wait = sim_rtcn_calb(64000,TMR_RTC1);
|
|
sim_activate(u75.unit,wait); /* 64000 ticks per second */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat u75_ckmode(I8253* chip,uint32 mode)
|
|
{
|
|
/* @TODO check valid modes */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat u75_call0(I8253* chip,int rw,uint32* value)
|
|
{
|
|
if (rw==1) {
|
|
I8253CNTR* cntr = &chip->cntr[0];
|
|
if ((cntr->mode & I8253_BOTH) && (cntr->state & I8253_ST_MSBNEXT)) {
|
|
sim_cancel(chip->unit);
|
|
return SCPE_OK; /* not fully loaded yet */
|
|
} else {
|
|
/* start the CK0 clock at 64000Hz */
|
|
sim_activate(chip->unit,sim_rtcn_init(64000,TMR_RTC1)); /* use timer1 C0 for this clock */
|
|
}
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/************************************************************************************
|
|
* timer 2
|
|
***********************************************************************************/
|
|
|
|
REG sagetimer2_reg[] = {
|
|
{ HRDATA(INIT, u74.init, 8), REG_HRO },
|
|
{ HRDATA(STATE0,u74.cntr[0].state, 8),REG_HRO },
|
|
{ HRDATA(STATE1,u74.cntr[1].state, 8),REG_HRO },
|
|
{ HRDATA(STATE2,u74.cntr[2].state, 8),REG_HRO },
|
|
{ HRDATA(MODE0, u74.cntr[0].mode, 8) },
|
|
{ HRDATA(MODE1, u74.cntr[1].mode, 8) },
|
|
{ HRDATA(MODE2, u74.cntr[2].mode, 8) },
|
|
{ HRDATA(CNT0, u74.cntr[0].count, 16) },
|
|
{ HRDATA(CNT1, u74.cntr[1].count, 16) },
|
|
{ HRDATA(CNT2, u74.cntr[2].count, 16) },
|
|
{ HRDATA(LATCH0,u74.cntr[0].latch, 16) },
|
|
{ HRDATA(LATCH1,u74.cntr[1].latch, 16) },
|
|
{ HRDATA(LATCH2,u74.cntr[2].latch, 16) },
|
|
{ HRDATA(DIV0, u74.cntr[0].divider, 16),REG_HRO },
|
|
{ HRDATA(DIV1, u74.cntr[1].divider, 16),REG_HRO },
|
|
{ HRDATA(DIV2, u74.cntr[2].divider, 16),REG_HRO },
|
|
{ NULL }
|
|
};
|
|
|
|
static MTAB sagetimer2_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "IO", "IO", &set_iobase, &show_iobase, NULL },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE sagetimer2_dev = {
|
|
"TIMER2", &sagetimer2_unit, sagetimer2_reg, sagetimer2_mod,
|
|
1, 16, 32, 2, 16, 16,
|
|
NULL, NULL, &sagetimer2_reset,
|
|
NULL, NULL, NULL,
|
|
&u74, DEV_DEBUG, 0,
|
|
i8253_dt, NULL, NULL
|
|
};
|
|
|
|
static t_stat sagetimer2_reset(DEVICE* dptr)
|
|
{
|
|
t_stat rc;
|
|
if ((rc = (dptr->flags & DEV_DIS) ?
|
|
del_iohandler(dptr->ctxt) :
|
|
add_iohandler(&sagetimer2_unit,dptr->ctxt,i8253_io)) != SCPE_OK) return rc;
|
|
return u74.reset(&u74);
|
|
}
|
|
|
|
static t_stat u74_ckmode(I8253* chip,uint32 mode)
|
|
{
|
|
/* @TODO check valid modes */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat u74_call1(I8253* chip,int rw,uint32* value)
|
|
{
|
|
if (rw==1) {
|
|
I8253CNTR* cntr = &chip->cntr[1];
|
|
if ((cntr->mode & I8253_BOTH) && (cntr->state & I8253_ST_MSBNEXT)) {
|
|
sim_cancel(chip->unit);
|
|
return SCPE_OK; /* not fully loaded yet */
|
|
} else {
|
|
/* start the CK0 clock at 64000Hz */
|
|
sim_activate(chip->unit,sim_rtcn_init(64000,TMR_RTC1)); /* use timer1 C0 for this clock */
|
|
}
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat timer2_svc(UNIT* uptr)
|
|
{
|
|
int32 wait;
|
|
I8253CNTR* t2c1 = &u74.cntr[1];
|
|
I8253CNTR* t2c2 = &u74.cntr[2];
|
|
|
|
/* we call this service 64000 times a second to decrement counter T2C1.
|
|
* When T2C1 reaches 0, it will decrement T2C2 */
|
|
t2c1->count--;
|
|
if (t2c1->count <= 0) {
|
|
/* reload from divider */
|
|
t2c1->count = t2c1->divider;
|
|
/* decrement T2C2 counter and raise interrupt 0 if counter is zero */
|
|
if (t2c2->count == 0) {
|
|
// printf("timer2 heartbeat\n");
|
|
sage_raiseint(TIMER2C2_PICINT);
|
|
}
|
|
t2c2->count--;
|
|
}
|
|
|
|
/* adjust timing */
|
|
wait = sim_rtcn_calb(64000,TMR_RTC1);
|
|
sim_activate(u74.unit,wait); /* 64000 ticks per second */
|
|
return SCPE_OK;
|
|
}
|