CID Action 1416081 changed variable answer to int 1416082 checked returned values with ASSURE - read error means corrupted target code. 1416088 added return 1416109 This fallthru was intentional - duplicated code to make coverity happy 1416111 This fallthru was intentional - duplicated code to make coverity happy 1416116 This fallthru was intentional - duplicated code to make coverity happy 1416117 This fallthru was intentional - duplicated code to make coverity happy 1416124 protected against negative return 1416142 added ASSURE, however this case won't happen since reg_intpending==true implies positive int level 1416145 checked non-NULL, return SCPE_ARG if NULL 1416150 since only 2 drives are supported, fdc_selected is decoded to 0 and 1 only (allowed 2 and 3 before) 1416152 restrict to 2 drives only 1416166 checked value with ASSURE 1416101 typo: should have been resx 1416106 unnecessary code removed 1416110 this fallthru was intentional - duplicated code to make coverity happy 1416112 this fallthru was intentional - duplicated code to make coverity happy 1416148 change condition to check for negative value 1416179 break was remainder from former logic - removed 1415866 code was remainder from former unimplemented instruction trap - removed
571 lines
18 KiB
C
571 lines
18 KiB
C
/* PDQ3_stddev.c: PDQ3 simulator standard devices
|
|
|
|
Work derived from Copyright (c) 2004-2012, Robert M. Supnik
|
|
Copyright (c) 2013 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
|
|
ROBERT M SUPNIK 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 names of Robert M Supnik and Holger Veit
|
|
shall not be used in advertising or otherwise to promote the sale, use or
|
|
other dealings in this Software without prior written authorization from
|
|
Robert M Supnik and Holger Veit.
|
|
|
|
2013xxxx hv initial version
|
|
20130902 hv added telnet multiplexer code
|
|
20131020 hv fixed CON interrupt handling
|
|
20131103 hv connect CON_ATTACH logic with DSR, so that DSR is set if tcp connect
|
|
20141003 hv compiler suggested warnings (vc++2013, gcc)
|
|
*/
|
|
#include "pdq3_defs.h"
|
|
#include <ctype.h>
|
|
|
|
extern UNIT cpu_unit;
|
|
extern UNIT con_unit[];
|
|
|
|
static t_stat con_termsvc(UNIT *uptr);
|
|
static t_stat con_pollsvc(UNIT *uptr);
|
|
static t_stat con_reset(DEVICE* dptr);
|
|
|
|
static t_stat tim_reset(DEVICE *dptr);
|
|
static t_stat tim0_svc(UNIT* uptr);
|
|
static t_stat tim1_svc(UNIT* uptr);
|
|
static t_stat tim2_svc(UNIT* uptr);
|
|
|
|
/* CON USART registers */
|
|
/* This is described as positive logic, and represents the
|
|
* content of the corresponding registers.
|
|
* However, the USART is connected to inverted DAL lines
|
|
* and needs to be written to with the inverted value
|
|
* This is done in CPU Read/Write */
|
|
#define CONC1_LOOP 0x80 /* 0=loopmode diagnostic, 1=normal full duplex */
|
|
#define CONC1_BRK 0x40 /* 1=send break */
|
|
#define CONC1_MISC 0x20 /* 1=one stop bit, 0=two */
|
|
#define CONC1_ECHO 0x10 /* 1=echo received data */
|
|
#define CONC1_PE 0x08 /* 1=check parity */
|
|
#define CONC1_RE 0x04 /* 1=enable receiver */
|
|
#define CONC1_RTS 0x02 /* 1=enable transmitter if CTS */
|
|
#define CONC1_DTR 0x01 /* 1=enable CD, DSR,RI interrupts */
|
|
static uint8 con_ctrl1;
|
|
#define CONC2_CLENMASK 0xc0 /* number of bits */
|
|
#define CONC2_CLEN8 0x00
|
|
#define CONC2_CLEN7 0x40
|
|
#define CONC2_CLEN6 0x80
|
|
#define CONC2_CLEN5 0xc0
|
|
#define CONC2_MODE 0x20 /* not used set to 0 (async mode) */
|
|
#define CONC2_ODDEVN 0x10 /* 0=even parity */
|
|
#define CONC2_RXCLK 0x08 /* not used, set to 1 */
|
|
#define CONC2_CLKMASK 0x07 /* clock selector */
|
|
#define CONC2_CLK110 0x06 /* must be set to 001 (rate 1 clock) */
|
|
static uint8 con_ctrl2;
|
|
#define CONS_DSC 0x80 /* set to 1 after status read, cleared by DSR/DCD/RI */
|
|
#define CONS_DSR 0x40 /* DSR input */
|
|
#define CONS_CD 0x20 /* DCD input */
|
|
#define CONS_FE 0x10 /* 1=framing error */
|
|
#define CONS_PE 0x08 /* 1=parity error */
|
|
#define CONS_OE 0x04 /* 1= overrun error */
|
|
#define CONS_DR 0x02 /* set to 1 if data received, 0 if data read */
|
|
#define CONS_THRE 0x01 /* set to 1 if data xmit buffer empty */
|
|
static uint8 con_status;
|
|
static uint8 con_xmit;
|
|
static uint8 con_rcv;
|
|
|
|
/************************************************************************************************
|
|
* Onboard Console
|
|
***********************************************************************************************/
|
|
|
|
/* con data structures
|
|
con_dev con device descriptor
|
|
con_unit con unit descriptor
|
|
con_mod con modifier list
|
|
con_reg con register list
|
|
*/
|
|
IOINFO con_ioinfo1 = { NULL, CON_IOBASE, 4, CON_RCV_VEC, 4, con_read, con_write };
|
|
IOINFO con_ioinfo2 = { &con_ioinfo1, 0, 0, CON_XMT_VEC, 3, con_read, con_write };
|
|
DEVCTXT con_ctxt = { &con_ioinfo2 };
|
|
|
|
UNIT con_unit[] = {
|
|
{ UDATA (&con_pollsvc, UNIT_ATTABLE, 0), CON_POLLRATE, },
|
|
{ UDATA (&con_termsvc, UNIT_IDLE, 0), CON_TERMRATE, }
|
|
};
|
|
static UNIT* con_tti = &con_unit[0]; /* shorthand for console input and output units */
|
|
static UNIT* con_tto = &con_unit[1];
|
|
|
|
REG con_reg[] = {
|
|
{ HRDATA (CTRL1, con_ctrl1, 8) },
|
|
{ HRDATA (CTRL2, con_ctrl2, 8) },
|
|
{ HRDATA (STAT, con_status, 8) },
|
|
{ HRDATA (XMIT, con_xmit, 8) },
|
|
{ HRDATA (RCV, con_rcv, 8) },
|
|
{ NULL }
|
|
};
|
|
MTAB con_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", NULL, &show_iobase },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", NULL, &show_iovec },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "PRIO", "PRIO", NULL, &show_ioprio },
|
|
{ 0 }
|
|
};
|
|
DEBTAB con_dflags[] = {
|
|
{ "WRITE", DBG_CON_WRITE },
|
|
{ "READ", DBG_CON_READ },
|
|
{ "SVC", DBG_CON_SVC },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
DEVICE con_dev = {
|
|
"CON", /*name*/
|
|
con_unit, /*units*/
|
|
con_reg, /*registers*/
|
|
con_mod, /*modifiers*/
|
|
2, /*numunits*/
|
|
16, /*aradix*/
|
|
16, /*awidth*/
|
|
1, /*aincr*/
|
|
8, /*dradix*/
|
|
8, /*dwidth*/
|
|
NULL, /*examine*/
|
|
NULL, /*deposit*/
|
|
&con_reset, /*reset*/
|
|
NULL, /*boot*/
|
|
NULL, /*attach*/
|
|
NULL, /*detach*/
|
|
&con_ctxt, /*ctxt*/
|
|
DEV_DEBUG|DEV_DISABLE, /*flags*/
|
|
0, /*dctrl*/
|
|
con_dflags, /*debflags*/
|
|
NULL, /*msize*/
|
|
NULL /*lname*/
|
|
};
|
|
|
|
/* bus reset handler */
|
|
t_stat con_binit() {
|
|
con_status = CONS_THRE;
|
|
setbit(con_status, CONS_DSR);
|
|
|
|
con_ctrl1 = 0; /* no echo, receiver disabled, transmitter disabled */
|
|
con_ctrl2 = 0; /* ASYNC mode, 8bits, Clock 1X */
|
|
con_xmit = 0;
|
|
con_rcv = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* common handlers */
|
|
static t_stat con_reset(DEVICE* dptr)
|
|
{
|
|
DEVCTXT* ctxt = (DEVCTXT*)dptr->ctxt;
|
|
int32 wait = con_tti->wait = CON_POLLRATE;
|
|
|
|
sim_rtcn_init (wait, TMR_CONPOLL); /* init poll timer */
|
|
|
|
sim_cancel(con_tto); /* disable output service */
|
|
|
|
/* register/deregister I/O handlers */
|
|
if (dptr->flags & DEV_DIS) {
|
|
del_ioh(ctxt->ioi);
|
|
} else {
|
|
add_ioh(ctxt->ioi);
|
|
con_tti->buf = 0;
|
|
sim_activate (con_tti, wait);
|
|
}
|
|
return con_binit();
|
|
}
|
|
|
|
t_stat con_attach(UNIT* uptr, char* cptr) {
|
|
setbit(con_status, CONS_DSR|CONS_DSC);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat con_detach(UNIT* uptr) {
|
|
clrbit(con_status, CONS_DSR);
|
|
setbit(con_status, CONS_DSC);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
#define XMITENABLED() (isbitset(con_ctrl1,CONC1_RTS))
|
|
#define XMITENABLE() setbit(con_ctrl1,CONC1_RTS)
|
|
#define XMITDISABLE() clrbit(con_ctrl1,CONC1_RTS)
|
|
#define XMITEMPTY() (isbitset(con_status, CONS_THRE))
|
|
|
|
#define RCVENABLED() (isbitset(con_ctrl1,CONC1_RE))
|
|
#define RCVFULL() (isbitset(con_status, CONS_DR))
|
|
#define RCVENABLE() setbit(con_ctrl1,CONC1_RE)
|
|
#define RCVDISABLE() clrbit(con_ctrl1,CONC1_RE)
|
|
|
|
#define DSRACTIVE() (isbitset(con_ctrl1,CONC1_DTR) && isbitset(con_status,CONS_DSR))
|
|
|
|
/* The transmit interrupt is raised continuously,
|
|
* as long as the transmit holding reg is empty and the transmitter is enabled.
|
|
* It will be deasserted when the tranmit reg is full or tranmitter disabled.
|
|
*/
|
|
#define XMITINTR() cpu_assertInt(INT_CONT, XMITEMPTY())
|
|
|
|
/* The receive interrupt is raised continuously,
|
|
* when the receiver holding register is full and the receiver is enabled.
|
|
* it will be deasserted when the receiver reg is read or the receiver disabled.
|
|
*/
|
|
#define RCVINTR() cpu_assertInt(INT_CONR, RCVFULL())
|
|
|
|
/* The DSR interrupt is raised when DSC is set to 1 (pos logic)
|
|
* and DTR is active, cleared if status is read */
|
|
#define DSRINTR() cpu_assertInt(INT_PRNT, DSRACTIVE())
|
|
|
|
/* Terminal output service */
|
|
t_stat con_termsvc (UNIT *uptr) {
|
|
t_stat rc = SCPE_OK;
|
|
|
|
int ch = sim_tt_outcvt(uptr->buf, TT_GET_MODE(uptr->flags));
|
|
if (XMITENABLED()) { /* tranmitter enabled */
|
|
if (ch >= 0) {
|
|
if ((rc = sim_putchar_s(ch)) != SCPE_OK) {
|
|
sim_activate(uptr, uptr->wait); /* did not emit char, reschedule termsvc */
|
|
return rc == SCPE_STALL ? SCPE_OK : rc;
|
|
}
|
|
}
|
|
}
|
|
uptr->pos = uptr->pos + 1;
|
|
setbit(con_status, CONS_THRE); /* set transmitter holding reg empty */
|
|
cpu_assertInt(INT_CONT, TRUE); /* generate an interrupt because of DRQO */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Terminal input service */
|
|
t_stat con_pollsvc(UNIT *uptr) {
|
|
int32 ch;
|
|
|
|
uptr->wait = sim_rtcn_calb(CON_TPS, TMR_CONPOLL); /* calibrate timer */
|
|
sim_activate(uptr, uptr->wait); /* restart polling */
|
|
|
|
if ((ch = sim_poll_kbd()) < SCPE_KFLAG) /* check keyboard */
|
|
return ch;
|
|
uptr->buf = (ch & SCPE_BREAK) ? 0 : sim_tt_inpcvt(ch, TT_GET_MODE(uptr->flags));
|
|
uptr->pos = uptr->pos + 1;
|
|
|
|
if (RCVENABLED()) { /* receiver enabled? */
|
|
if (RCVFULL()) /* handle data overrun */
|
|
setbit(con_status, CONS_OE);
|
|
|
|
con_rcv = ch & 0xff; /* put in receiver register */
|
|
setbit(con_status, CONS_DR); /* notify: data received */
|
|
cpu_assertInt(INT_CONR, TRUE); /* generate interrupt because of DRQI */
|
|
|
|
if (isbitset(con_ctrl1, CONC1_ECHO)) { /* echo? XXX handle in telnet handler? */
|
|
/* XXX use direct send here, not sending via con_termsvc */
|
|
return sim_putchar_s(ch);
|
|
}
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static int set_parity(int c, int odd)
|
|
{
|
|
int i, p = 0;
|
|
for (i=0; i<8; i++)
|
|
if (c & (1<<i)) p ^= 1;
|
|
c |= p ? 0 : 0x80;
|
|
if (!odd) c ^= 0x80;
|
|
return c;
|
|
}
|
|
#if 0
|
|
// currently unused
|
|
static int get_parity(int c, int even)
|
|
{
|
|
int i, p = 0;
|
|
for (i=0; i<8; i++)
|
|
if (c & (1<<i)) p ^= 1;
|
|
if (even) p ^= 1;
|
|
return p;
|
|
}
|
|
#endif
|
|
|
|
// functions from memory handler to read and write a char
|
|
// note: the usart is connected to inverted data lines,
|
|
// this is fixed by negating input and output
|
|
//
|
|
// The logic in here uses the positive logic conventions as
|
|
// described in the WD1931 data sheet, not the ones in the PDQ-3_Hardware_Users_Manual
|
|
t_stat con_write(t_addr ioaddr, uint16 data) {
|
|
|
|
/* note usart has inverted bus, so all data is inverted */
|
|
data = (~data) & 0xff;
|
|
switch (ioaddr & 0x0003) {
|
|
case 0: /* CTRL1 */
|
|
con_ctrl1 = data & 0xff;
|
|
if (!RCVENABLED()) { /* disable receiver */
|
|
clrbit(con_status,CONS_FE|CONS_PE|CONS_OE|CONS_DR);
|
|
sim_cancel(con_tti);
|
|
} else {
|
|
sim_activate(con_tti, con_tti->wait); /* start poll service, will raise interrupt if buffer full */
|
|
}
|
|
if (!XMITENABLED()) { /* disable transmitter */
|
|
/* will drain current pending xmit service. RTS output is assumed to become inactive
|
|
* (it is not necessary to emulate it) */
|
|
} else {
|
|
if (XMITEMPTY()) {
|
|
} else {
|
|
/* some char in THR, start service to emit */
|
|
sim_activate(con_tto, con_tto->wait);
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
con_ctrl2 = data & 0xff;
|
|
break;
|
|
case 2:
|
|
// ignore this here - DLE register
|
|
break;
|
|
case 3:
|
|
switch (con_ctrl2 & CONC2_CLENMASK) {
|
|
case CONC2_CLEN5: data &= 0x1f; break;
|
|
case CONC2_CLEN6: data &= 0x3f; break;
|
|
case CONC2_CLEN7: data &= 0x7f;
|
|
if (isbitset(con_ctrl1,CONC1_PE))
|
|
data = set_parity(data, con_ctrl2 & CONC2_ODDEVN);
|
|
break;
|
|
case CONC2_CLEN8: data &= 0xff; break;
|
|
}
|
|
con_xmit = data & 0xff;
|
|
con_tto->buf = data;
|
|
clrbit(con_status,CONS_THRE);
|
|
if (XMITENABLED())
|
|
sim_activate(con_tto, con_tto->wait);
|
|
}
|
|
// RCVINTR();
|
|
XMITINTR();
|
|
DSRINTR();
|
|
|
|
sim_debug(DBG_CON_WRITE, &con_dev, DBG_PCFORMAT0 "Byte write %02x (pos logic) to $%04x\n", DBG_PC, data & 0xff, ioaddr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat con_read(t_addr ioaddr, uint16 *data) {
|
|
switch (ioaddr & 0x0003) {
|
|
case 0: /* CTRL1 */
|
|
*data = con_ctrl1;
|
|
break;
|
|
case 1:
|
|
*data = con_ctrl2;
|
|
break;
|
|
case 2:
|
|
/* XXX find out whether terminal is attached to console? */
|
|
setbit(con_status, CONS_DSR);
|
|
// else clrbit(con_status, CONS_DSR);
|
|
*data = con_status;
|
|
clrbit(con_status,CONS_DSC); /* acknowledge change in DSR/DCD */
|
|
break;
|
|
case 3:
|
|
*data = con_rcv;
|
|
clrbit(con_status,CONS_DR);
|
|
cpu_assertInt(INT_CONR, FALSE);
|
|
}
|
|
sim_debug(DBG_CON_READ, &con_dev, DBG_PCFORMAT1 "Byte read %02x (pos logic) from $%04x\n", DBG_PC, *data & 0xff, ioaddr);
|
|
|
|
/* note usart has inverted bus, so returned data must be negated */
|
|
*data = ~(*data);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* Onboard 8253 timer
|
|
***********************************************************************************************/
|
|
|
|
struct i8253 {
|
|
uint16 cnt;
|
|
uint16 preset;
|
|
uint16 mode;
|
|
t_bool hilo; /* which half of 16 bit cnt is to be set */
|
|
};
|
|
struct i8253 tim[3];
|
|
|
|
IOINFO tim_ioinfo1 = { NULL, TIM_IOBASE, 4, TIM_TICK_VEC, 6, tim_read, tim_write };
|
|
IOINFO tim_ioinfo2 = { &tim_ioinfo1, 0, 0, TIM_INTVL_VEC, 7, tim_read, tim_write };
|
|
DEVCTXT tim_ctxt = { &tim_ioinfo2 };
|
|
|
|
UNIT tim_unit[] = {
|
|
{ UDATA (&tim0_svc, 0, 0), CON_POLLRATE, },
|
|
{ UDATA (&tim1_svc, 0, 0), CON_POLLRATE, },
|
|
{ UDATA (&tim2_svc, 0, 0), CON_POLLRATE, }
|
|
};
|
|
|
|
REG tim_reg[] = {
|
|
{ HRDATA (CNT0, tim[0].cnt, 16) },
|
|
{ HRDATA (CNT1, tim[1].cnt, 16) },
|
|
{ HRDATA (CNT2, tim[2].cnt, 16) },
|
|
{ HRDATA (MODE0, tim[0].mode, 8) },
|
|
{ HRDATA (MODE1, tim[1].mode, 8) },
|
|
{ HRDATA (MODE2, tim[2].mode, 8) },
|
|
{ NULL }
|
|
};
|
|
MTAB tim_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", NULL, &show_iobase },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", NULL, &show_iovec },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "PRIO", "PRIO", NULL, &show_ioprio },
|
|
{ 0 }
|
|
};
|
|
DEBTAB tim_dflags[] = {
|
|
{ "WRITE", DBG_TIM_WRITE },
|
|
{ "READ", DBG_TIM_READ },
|
|
{ "SVC", DBG_TIM_SVC },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
DEVICE tim_dev = {
|
|
"TIM", /*name*/
|
|
tim_unit, /*units*/
|
|
tim_reg, /*registers*/
|
|
tim_mod, /*modifiers*/
|
|
3, /*numunits*/
|
|
16, /*aradix*/
|
|
16, /*awidth*/
|
|
1, /*aincr*/
|
|
8, /*dradix*/
|
|
8, /*dwidth*/
|
|
NULL, /*examine*/
|
|
NULL, /*deposit*/
|
|
&tim_reset, /*reset*/
|
|
NULL, /*boot*/
|
|
NULL, /*attach*/
|
|
NULL, /*detach*/
|
|
&tim_ctxt, /*ctxt*/
|
|
DEV_DEBUG, /*flags*/
|
|
0, /*dctrl*/
|
|
tim_dflags, /*debflags*/
|
|
NULL, /*msize*/
|
|
NULL /*lname*/
|
|
};
|
|
|
|
t_stat tim_reset(DEVICE *dptr)
|
|
{
|
|
DEVCTXT* ctxt = (DEVCTXT*)dptr->ctxt;
|
|
|
|
if (dptr->flags & DEV_DIS) {
|
|
del_ioh(ctxt->ioi);
|
|
sim_cancel(&tim_unit[0]);
|
|
sim_cancel(&tim_unit[1]);
|
|
sim_cancel(&tim_unit[2]);
|
|
} else {
|
|
add_ioh(ctxt->ioi);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat tim_read(t_addr ioaddr, uint16 *data)
|
|
{
|
|
int n = ioaddr & 0x0003;
|
|
if (n == 3)
|
|
*data = 0xff;
|
|
else {
|
|
*data = (tim[n].hilo ? tim[n].cnt : (tim[n].cnt >> 8)) & 0xff;
|
|
sim_debug(DBG_TIM_READ, &tim_dev, DBG_PCFORMAT1 "Read %s timer%d: %02x\n",
|
|
DBG_PC, tim[n].hilo ? "high" : "low", n, *data);
|
|
tim[n].hilo = ! tim[n].hilo;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static uint16 sethi(uint16 val, uint16 data) {
|
|
val &= 0xff;
|
|
val |= (data << 8);
|
|
return val;
|
|
}
|
|
static uint16 setlo(uint16 val, uint16 data) {
|
|
val &= 0xff00;
|
|
val |= data;
|
|
return val;
|
|
}
|
|
t_stat tim_write(t_addr ioaddr, uint16 data)
|
|
{
|
|
int n = ioaddr & 0x0003;
|
|
data &= 0xff;
|
|
if (n == 3) {
|
|
n = (data & 0xc0) >> 6;
|
|
sim_debug(DBG_TIM_WRITE, &tim_dev, DBG_PCFORMAT0 "Timer%d: mode=%d\n",
|
|
DBG_PC, n, (data >> 1) & 7);
|
|
if (n == 3) {
|
|
sim_printf("Unimplemented: Mode=0xc0\n");
|
|
return STOP_IMPL;
|
|
}
|
|
if (data & 0x01) {
|
|
sim_printf("Unimplemented: BCD mode: timer=%d\n",n);
|
|
return STOP_IMPL;
|
|
}
|
|
if (!( (data & 0x0e)==0x00 || (data & 0x0e)==0x04)) {
|
|
sim_printf("Unimplemented: Mode not 0 or 2: timer=%d\n",n);
|
|
return STOP_IMPL;
|
|
}
|
|
if ((data & 0x30) != 0x30) {
|
|
sim_printf("Unimplemented: not 16 bit load: timer=%d\n",n);
|
|
return STOP_IMPL;
|
|
}
|
|
tim[n].mode = data;
|
|
} else {
|
|
if (tim[n].hilo) {
|
|
tim[n].preset = sethi(tim[n].preset, data);
|
|
tim[n].cnt = sethi(tim[n].cnt, data);
|
|
if (n < 2) { /* timer 2 is triggered by timer 1 */
|
|
int32 time = 1250000 / tim[n].cnt;
|
|
sim_cancel(&tim_unit[n]);
|
|
sim_activate(&tim_unit[n], time);
|
|
}
|
|
} else {
|
|
tim[n].preset = setlo(tim[n].preset, data);
|
|
tim[n].cnt = setlo(tim[n].cnt, data);
|
|
}
|
|
sim_debug(DBG_TIM_WRITE, &tim_dev, DBG_PCFORMAT0 "Timer%d: %s cnt=%02x\n",
|
|
DBG_PC, n, tim[n].hilo ? "high":"low", data);
|
|
tim[n].hilo = !tim[n].hilo;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* baud rate timer 0 is programmed in mode 2 - actually, this is ignored */
|
|
static t_stat tim0_svc(UNIT* uptr)
|
|
{
|
|
int32 time = 1250000 / tim[0].preset;
|
|
sim_activate(uptr, time);
|
|
sim_debug(DBG_TIM_SVC, &tim_dev, DBG_PCFORMAT2 "Timer0: SVC call\n", DBG_PC);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* system timer 1 is programmed in mode 2, causes interrupt each time it is 0 */
|
|
static t_stat tim1_svc(UNIT* uptr)
|
|
{
|
|
int32 time = 1250000 / tim[0].preset;
|
|
sim_debug(DBG_TIM_SVC, &tim_dev, DBG_PCFORMAT2 "Timer1: SVC call\n", DBG_PC);
|
|
sim_activate(uptr, time);
|
|
cpu_raiseInt(INT_TICK);
|
|
reg_ssr |= SSR_TICK; /* notify TICK timer int occurred */
|
|
|
|
/* handle interval timer */
|
|
if (tim[2].cnt > 0) tim[2].cnt--;
|
|
if (tim[2].cnt == 0) {
|
|
cpu_raiseInt(INT_INTVL);
|
|
reg_ssr |= SSR_INTVL; /* notify INTVL timer int occurred */
|
|
if ((tim[2].mode & 0x0e) == 0x04) {
|
|
tim[2].cnt = tim[2].preset; /* restart timer */
|
|
} /* otherwise single shot */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* interval timer 2 is programmed in mode 0 (single shot) or 2 (rate generator)
|
|
* this is triggered by timer1 - svc is ignored here */
|
|
static t_stat tim2_svc(UNIT* uptr)
|
|
{
|
|
sim_debug(DBG_TIM_SVC, &tim_dev, DBG_PCFORMAT2 "Timer2: SVC call - should not occur\n", DBG_PC);
|
|
return SCPE_OK;
|
|
}
|
|
|