/* i0.c: Intel intellec imm8-60 Copyright (c) 2020, William A. Beech 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 WILLIAM A. BEECH 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 William A. Beech shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from William A. Beech. MODIFICATIONS: 18 July 20 - Original file. NOTES: These functions support a simulated imm8-60 interface board attached to a Teletype Model 33 ASR. */ #include "system_defs.h" #if defined (IO_NUM) && (IO_NUM > 0) // imm8-60 status bits // I/O COMMAND CONSTANTS #define RBIT 1 //TTY READER GO/NO GO #define PCMD 2 //PTP GO/NO GO #define RCMD 4 //PTR GO/NO GO #define DSB 8 //PROM ENABLE/DISABLE. DSB=1 #define XXX 0x10 //DATA IN T/C #define XXY 0x20 //DATA OUT T/C #define PBIT 0x40 //1702 PROM PROG. GO/NO GO #define PBITA 0x80 //1702A PROM PROG. GO/NO GO // TTY I/O CONSTANTS #define TTI 0 //TTY INPUT DATA PORT #define TTO 0 //TTY OUTPUT DATA PORT #define TTS 1 //TTY INPUT STATUS PORT #define TTC 1 //TTY OUTPUT COMMAND PORT #define TTYGO RBIT OR DSB //START TTY READER #define TTYNO DSB //STOP TTY READER #define TTYDA 1 //DATA AVAILABLE #define TTYBE 4 //TRANSMIT BUFFER EMPTY // CRT I/O CONSTANTS #define CRTI 4 //CRT INPUT DATA PORT #define CRTS 5 //CRT INPUT STATUS PORT #define CRTO 4 //CRT OUTPUT DATA PORT #define CRTDA 1 //DATA AVAILABLE #define CRTBE 4 //TRANSMIT BUFFER EMPTY // PTR I/O CONSTANTS #define PTRI 3 //PTR INPUT DATA PORT (NOT INVERTED) #define PTRS TTS //PTR INPUT STATUS PORT #define PTRC TTC //PTR OUTPUT COMMAND PORT #define PTRGO RCMD OR DSB //START PTR #define PTRNO TTYNO //STOP PTR #define PTRDA 0x20 //PTR DATA AVAILABLE // PTP I/O CONSTANTS #define PTPO 3 //PTP OUTPUT DATA PORT #define PTPS TTS //PTP INPUT STATUS PORT #define PTPC TTC //PTP OUTPUT COMMAND PORT #define PRDY 0x40 //PUNCH READY STATUS #define PTPGO PCMD OR DSB //PTP START PUNCH #define PTPNO TTYNO //STOP PUNCH // PROM PROGRAMMER I/O CONSTANTS #define PAD 2 //PROM ADDRES OUTPUT PORT #define PDO PTPO //PROM DATA OUTPUT PORT #define PDI 2 //PROM DATA INPUT PORT #define PROMC TTC //PROGRAMMING PULSE OUTPUT PORT #define PROGO PBITA //START PROGRAMMING #define PRONO 0 //STOP PROGRAMMING #define ENB 0 //ENABLE PROGRAMMER /* external globals */ /* external function prototypes */ extern uint8 reg_dev(uint8 (*routine)(t_bool, uint8, uint8), uint8, uint8); /* globals */ uint8 status = 0; uint8 command = 0; /* function prototypes */ t_stat IO_cfg(uint8 base, uint8 devnum); t_stat IO_svc (UNIT *uptr); t_stat IO_reset (DEVICE *dptr); t_stat IO_attach (UNIT *uptr, CONST char *cptr); t_stat PTR_reset(DEVICE *dptr); t_stat PTR_attach (UNIT *uptr, CONST char *cptr); uint8 IO_is(t_bool io, uint8 data, uint8 devnum); uint8 IO_id(t_bool io, uint8 data, uint8 devnum); uint8 IO_oc(t_bool io, uint8 data, uint8 devnum); uint8 IO_od(t_bool io, uint8 data, uint8 devnum); void IO_reset_dev(uint8 devnum); /* imm-60 Standard I/O Data Structures */ UNIT IO_unit[4] = { { UDATA (&IO_svc, 0, 0), 10 }, //TTY input/output { UDATA (&IO_svc, 0, 0), 10 }, //TTY status/command { UDATA (&IO_svc, 0, 0), KBD_POLL_WAIT }, //PROM data input/output { UDATA (0, UNIT_ATTABLE+UNIT_DISABLE+UNIT_BUFABLE+UNIT_MUSTBUF, 0x1000) } //TTY reader/punch }; REG IO_reg[] = { { HRDATA (DATA0, IO_unit[0].buf, 8) }, { HRDATA (STAT0, status, 8) }, { HRDATA (MODE0, IO_unit[0].u4, 8) }, { HRDATA (CMD0, IO_unit[0].u5, 8) }, { HRDATA (DATA1, IO_unit[1].buf, 8) }, { HRDATA (STAT1, status, 8) }, { HRDATA (MODE1, IO_unit[1].u4, 8) }, { HRDATA (CMD1, IO_unit[1].u5, 8) }, { HRDATA (DATA2, IO_unit[2].buf, 8) }, { HRDATA (STAT2, status, 8) }, { HRDATA (MODE2, IO_unit[2].u4, 8) }, { HRDATA (CMD2, IO_unit[2].u5, 8) }, { HRDATA (DATA3, IO_unit[3].buf, 8) }, { HRDATA (STAT3, status, 8) }, { HRDATA (MODE3, IO_unit[3].u4, 8) }, { HRDATA (CMD3, IO_unit[3].u5, 8) }, { NULL } }; DEBTAB IO_debug[] = { { "ALL", DEBUG_all }, { "FLOW", DEBUG_flow }, { "READ", DEBUG_read }, { "WRITE", DEBUG_write }, { "XACK", DEBUG_xack }, { "LEV1", DEBUG_level1 }, { "LEV2", DEBUG_level2 }, { NULL } }; MTAB IO_mod[] = { { 0 } }; /* address width is set to 16 bits to use devices in 8086/8088 implementations */ DEVICE IO_dev = { "IO", //name IO_unit, //units IO_reg, //registers IO_mod, //modifiers IO_NUM, //numunits 16, //aradix 16, //awidth 1, //aincr 16, //dradix 8, //dwidth NULL, //examine NULL, //deposit &IO_reset, //reset NULL, //boot &IO_attach, //attach NULL, //detach NULL, //ctxt 0, //flags 0, //dctrl IO_debug, //debflags NULL, //msize NULL //lname }; UNIT PTR_unit[1] = { { UDATA (0, UNIT_ATTABLE+UNIT_DISABLE+UNIT_BUFABLE+UNIT_MUSTBUF, 0x1000) } //TTY reader/punch }; REG PTR_reg[] = { { HRDATA (DATA0, IO_unit[0].buf, 8) }, { HRDATA (STAT0, status, 8) }, { HRDATA (MODE0, IO_unit[0].u4, 8) }, { HRDATA (CMD0, IO_unit[0].u5, 8) }, { NULL } }; DEBTAB PTR_debug[] = { { "ALL", DEBUG_all }, { "FLOW", DEBUG_flow }, { "READ", DEBUG_read }, { "WRITE", DEBUG_write }, { "XACK", DEBUG_xack }, { "LEV1", DEBUG_level1 }, { "LEV2", DEBUG_level2 }, { NULL } }; MTAB PTR_mod[] = { { 0 } }; /* address width is set to 16 bits to use devices in 8086/8088 implementations */ DEVICE PTR_dev = { "PTR", //name PTR_unit, //units PTR_reg, //registers PTR_mod, //modifiers PTR_NUM, //numunits 16, //aradix 16, //awidth 1, //aincr 16, //dradix 8, //dwidth NULL, //examine NULL, //deposit &PTR_reset, //reset NULL, //boot &PTR_attach, //attach NULL, //detach NULL, //ctxt 0, //flags 0, //dctrl PTR_debug, //debflags NULL, //msize NULL //lname }; // imm-60 configuration t_stat IO_cfg(uint8 base, uint8 devnum) { sim_printf(" io[%d]: at base port 0%02XH\n", devnum, base & 0xFF); reg_dev(IO_id, base, devnum); reg_dev(IO_is, base + 1, devnum); reg_dev(IO_oc, base + 2, devnum); reg_dev(IO_od, base + 3, devnum); return SCPE_OK; } /* Service routines to handle simulator functions */ /* IO_svc - actually gets char & places in buffer */ t_stat IO_svc (UNIT *uptr) { int32 temp; sim_activate (uptr, uptr->wait); /* continue poll */ if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) { status |= TTYDA; //clear data avail return temp; /* no char or error? */ } // if (command & RBIT) { //read from tty rdr // printf("%c", (int)(uptr+3)->filebuf); // } uptr->buf = toupper(temp & 0x7F); /* Save char */ status &= ~TTYDA; /* Set data available status */ return SCPE_OK; } /* Reset routine */ t_stat IO_reset (DEVICE *dptr) { uint8 devnum; for (devnum=0; devnum < IO_NUM; devnum++) { IO_reset_dev(devnum); sim_activate (&IO_unit[devnum], IO_unit[devnum].wait); /* activate unit */ } return SCPE_OK; } void IO_reset_dev(uint8 devnum) { status = TTYDA | PTRDA | DSB; /* set data not avail status */ IO_unit[devnum].u4 = 0; IO_unit[devnum].u5 = 0; IO_unit[devnum].u6 = 0; IO_unit[devnum].buf = 0; IO_unit[devnum].pos = 0; } t_stat IO_attach (UNIT *uptr, CONST char *cptr) { t_stat r; if ((r = attach_unit (uptr, cptr)) != SCPE_OK) { sim_printf(" IO_attach: Attach error %d\n", r); return r; } return SCPE_OK; } t_stat PTR_reset(DEVICE *dptr) { return SCPE_OK; } t_stat PTR_attach (UNIT *uptr, CONST char *cptr) { t_stat r; if ((r = attach_unit (uptr, cptr)) != SCPE_OK) { sim_printf(" PTR_attach: Attach error %d\n", r); return r; } return SCPE_OK; } /* I/O instruction handlers, called from the CPU module when an IN or OUT instruction is issued. */ // status/command uint8 IO_is(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read status port - works*/ return status; } else { /* write command port */ command = data; if (command & RBIT) { status &= ~TTYDA; /* Set data available status */ data = data; } } return 0; } // TTY in/out uint8 IO_id(t_bool io, uint8 data, uint8 devnum) { char val; if (io == 0) { /* read data port */ if (command & RBIT) { //read from tty rdr status |= TTYDA; //set TTYDA off return 'Z'; } else { status |= TTYDA; //set TTYDA off val = IO_unit[devnum].buf; val = (~val) & 0x7f; return (val); } } else { /* write data port - works*/ // IO_unit[devnum].u3 |= TTYBE; //set TTYBE off val = ~data; sim_putchar(val & 0x7f); } return 0; } uint8 IO_oc(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read status port */ data = data; } else { /* write status port */ data = data; } return 0; } // TTY RDR in/PCH out uint8 IO_od(t_bool io, uint8 data, uint8 devnum) { char val; if (io == 0) { /* read data port */ status |= PTRDA; //set PTRDA off val = IO_unit[devnum].buf; val = (~val) & 0x7f; return (val); } else { /* write data port - works*/ data = data; // IO_unit[devnum].u3 |= TTYBE; //set TTYBE off // val = ~data; // sim_putchar(val & 0x7f); } return 0; } #endif /* IO_NUM > 0 */ /* end of imm8-60.c */