| /* id4_stddev.c: Interdata 4 standard devices | |
| Copyright (c) 1993-2000, Robert M. Supnik | |
| 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 name of Robert M Supnik 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. | |
| pt paper tape reader and punch | |
| tt console | |
| cdr card reader | |
| */ | |
| #include "id4_defs.h" | |
| #include <ctype.h> | |
| #define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */ | |
| #define UNIT_UC (1 << UNIT_V_UC) | |
| extern unsigned int16 M[]; | |
| extern int32 int_req[INTSZ], int_enb[INTSZ]; | |
| int32 pt_run = 0, pt_slew = 0; /* ptr modes */ | |
| int pt_rw = 0, pt_busy = 0; /* pt state */ | |
| int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ | |
| int32 tt_hdpx = 0; /* tt mode */ | |
| int32 tt_rw = 0, tt_busy = 0; /* tt state */ | |
| t_stat ptr_svc (UNIT *uptr); | |
| t_stat ptp_svc (UNIT *uptr); | |
| t_stat pt_boot (int32 unitno); | |
| t_stat pt_reset (DEVICE *dptr); | |
| t_stat tti_svc (UNIT *uptr); | |
| t_stat tto_svc (UNIT *uptr); | |
| t_stat tt_reset (DEVICE *dptr); | |
| extern t_stat sim_poll_kbd (void); | |
| extern t_stat sim_putchar (int32 out); | |
| /* Device definitions */ | |
| #define PTR 0 | |
| #define PTP 1 | |
| #define PT_STA_OVFL 0x80 /* overflow */ | |
| #define PT_STA_NMTN 0x10 /* no motion */ | |
| #define PT_V_RUN 4 /* run/stop */ | |
| #define PT_M_RUN 0x3 | |
| #define PT_RUN 1 | |
| #define PT_STOP 2 | |
| #define PT_CRS 3 | |
| #define PT_GETRUN(x) (((x) >> PT_V_RUN) & PT_M_RUN) | |
| #define PT_V_SLEW 2 /* slew/step */ | |
| #define PT_M_SLEW 0x3 | |
| #define PT_SLEW 1 | |
| #define PT_STEP 2 | |
| #define PT_CSLEW 3 | |
| #define PT_GETSLEW(x) (((x) >> PT_V_SLEW) & PT_M_SLEW) | |
| #define PT_V_RW 0 /* read/write */ | |
| #define PT_M_RW 0x3 | |
| #define PT_RD 1 | |
| #define PT_WD 2 | |
| #define PT_CRW 3 | |
| #define PT_GETRW(x) (((x) >> PT_V_RW) & PT_M_RW) | |
| #define TTI 0 | |
| #define TTO 1 | |
| #define TT_V_DPX 4 /* half/full duplex */ | |
| #define TT_M_DPX 0x3 | |
| #define TT_FDPX 1 | |
| #define TT_HDPX 2 | |
| #define TT_CDPX 3 | |
| #define TT_GETDPX(x) (((x) >> TT_V_DPX) & TT_M_DPX) | |
| #define TT_V_RW 2 /* read/write */ | |
| #define TT_M_RW 0x3 | |
| #define TT_RD 1 | |
| #define TT_WD 2 | |
| #define TT_CRW 3 | |
| #define TT_GETRW(x) (((x) >> TT_V_RW) & TT_M_RW) | |
| /* PT data structures | |
| pt_dev PT device descriptor | |
| pt_unit PT unit descriptors | |
| pt_reg PT register list | |
| */ | |
| UNIT pt_unit[] = { | |
| { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_IN_WAIT }, | |
| { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT } | |
| }; | |
| REG pt_reg[] = { | |
| { HRDATA (RBUF, pt_unit[PTR].buf, 8) }, | |
| { DRDATA (RPOS, pt_unit[PTR].pos, 31), PV_LEFT }, | |
| { DRDATA (RTIME, pt_unit[PTR].wait, 24), PV_LEFT }, | |
| { FLDATA (RSTOP_IOE, ptr_stopioe, 0) }, | |
| { HRDATA (PBUF, pt_unit[PTP].buf, 8) }, | |
| { DRDATA (PPOS, pt_unit[PTP].pos, 31), PV_LEFT }, | |
| { DRDATA (PTIME, pt_unit[PTP].wait, 24), PV_LEFT }, | |
| { FLDATA (PSTOP_IOE, ptp_stopioe, 0) }, | |
| { FLDATA (IREQ, int_req[PT/32], PT & 0x1F) }, | |
| { FLDATA (IENB, int_enb[PT/32], PT & 0x1F) }, | |
| { FLDATA (RUN, pt_run, 0) }, | |
| { FLDATA (SLEW, pt_slew, 0) }, | |
| { FLDATA (BUSY, pt_busy, 0) }, | |
| { FLDATA (RW, pt_rw, 0) }, | |
| { NULL } }; | |
| DEVICE pt_dev = { | |
| "PT", pt_unit, pt_reg, NULL, | |
| 2, 10, 31, 1, 8, 8, | |
| NULL, NULL, &pt_reset, | |
| &pt_boot, NULL, NULL }; | |
| /* TT data structures | |
| tt_dev TT device descriptor | |
| tt_unit TT unit descriptors | |
| tt_reg TT register list | |
| tt_mod TT modifiers list | |
| */ | |
| UNIT tt_unit[] = { | |
| { UDATA (&tti_svc, UNIT_UC, 0), KBD_POLL_WAIT }, | |
| { UDATA (&tto_svc, UNIT_UC, 0), SERIAL_OUT_WAIT } | |
| }; | |
| REG tt_reg[] = { | |
| { HRDATA (KBUF, tt_unit[TTI].buf, 8) }, | |
| { DRDATA (KPOS, tt_unit[TTI].pos, 31), PV_LEFT }, | |
| { DRDATA (KTIME, tt_unit[TTI].wait, 24), REG_NZ + PV_LEFT }, | |
| { HRDATA (TBUF, tt_unit[TTO].buf, 8) }, | |
| { DRDATA (TPOS, tt_unit[TTO].pos, 31), PV_LEFT }, | |
| { DRDATA (TTIME, tt_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, | |
| { FLDATA (IREQ, int_req[TT/32], TT & 0x1F) }, | |
| { FLDATA (IENB, int_enb[TT/32], TT & 0x1F) }, | |
| { FLDATA (HDPX, tt_hdpx, 0) }, | |
| { FLDATA (BUSY, tt_busy, 0) }, | |
| { FLDATA (RW, tt_rw, 0) }, | |
| { FLDATA (UC, tt_unit[TTI].flags, UNIT_V_UC), REG_HRO }, | |
| { NULL } }; | |
| MTAB tt_mod[] = { | |
| { UNIT_UC, 0, "lower case", "LC", NULL }, | |
| { UNIT_UC, UNIT_UC, "upper case", "UC", NULL }, | |
| { 0 } }; | |
| DEVICE tt_dev = { | |
| "TT", tt_unit, tt_reg, tt_mod, | |
| 2, 10, 31, 1, 8, 8, | |
| NULL, NULL, tt_reset, | |
| NULL, NULL, NULL }; | |
| /* Paper tape: IO routine */ | |
| int32 pt (int32 op, int32 dat) | |
| { | |
| int32 t; | |
| switch (op) { /* case IO op */ | |
| case IO_ADR: /* select */ | |
| break; | |
| case IO_OC: /* command */ | |
| t = CMD_GETINT (dat); /* get enable */ | |
| if (t == CMD_IENB) SET_ENB (PT); /* process */ | |
| else if (t == CMD_IDIS) CLR_ENB (PT); | |
| else if (t == CMD_ICOM) COM_ENB (PT); | |
| t = PT_GETRUN (dat); /* run/stop */ | |
| if (t == PT_RUN) pt_run = 1; /* process */ | |
| else if (t == PT_STOP) pt_run = 0; | |
| else if (t == PT_CRS) pt_run = pt_run ^ 1; | |
| t = PT_GETSLEW (dat); /* slew/step */ | |
| if (t == PT_SLEW) pt_slew = 1; /* process */ | |
| else if (t == PT_STEP) pt_slew = 0; | |
| else if (t == PT_CSLEW) pt_slew = pt_slew ^ 1; | |
| t = PT_GETRW (dat); /* read/write */ | |
| if (t == PT_RD) pt_rw = 0; /* process */ | |
| else if (t == PT_WD) pt_rw = 1; | |
| else if (t == PT_CRW) pt_rw = pt_rw ^ 1; | |
| if ((pt_rw == 0) && pt_run) /* read + run? */ | |
| sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); | |
| if (sim_is_active (&pt_unit[pt_rw? PTP: PTR])) pt_busy = 1; | |
| else { if (pt_busy) SET_INT (PT); | |
| pt_busy = 0; } | |
| break; | |
| case IO_RD: /* read */ | |
| if (pt_run && !pt_slew) | |
| sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); | |
| if (pt_rw == 0) pt_busy = 1; | |
| return (pt_unit[PTR].buf & 0xFF); | |
| case IO_WD: /* write */ | |
| pt_unit[PTP].buf = dat & 0xFF; | |
| sim_activate (&pt_unit[PTP], pt_unit[PTP].wait); | |
| if (pt_rw) pt_busy = 1; | |
| break; | |
| case IO_SS: /* status */ | |
| t = pt_busy? STA_BSY: 0; | |
| if ((pt_unit[PTR].flags & UNIT_ATT) == 0) t = t | STA_DU; | |
| if (!sim_is_active (&pt_unit[PTR])) t = t | PT_STA_NMTN | STA_EX; | |
| return t; } | |
| return 0; | |
| } | |
| /* Unit service */ | |
| t_stat ptr_svc (UNIT *uptr) | |
| { | |
| int32 temp; | |
| if ((pt_unit[PTR].flags & UNIT_ATT) == 0) /* attached? */ | |
| return IORETURN (ptr_stopioe, SCPE_UNATT); | |
| if (pt_rw == 0) { /* read mode? */ | |
| if (pt_busy) SET_INT (PT); /* if busy, intr */ | |
| pt_busy = 0; } /* not busy */ | |
| if (pt_slew) sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); /* slew? */ | |
| if ((temp = getc (pt_unit[PTR].fileref)) == EOF) { | |
| if (feof (pt_unit[PTR].fileref)) { | |
| if (ptr_stopioe) printf ("PTR end of file\n"); | |
| else return SCPE_OK; } | |
| else perror ("PTR I/O error"); | |
| clearerr (pt_unit[PTR].fileref); | |
| return SCPE_IOERR; } | |
| pt_unit[PTR].buf = temp & 0xFF; | |
| pt_unit[PTR].pos = pt_unit[PTR].pos + 1; | |
| return SCPE_OK; | |
| } | |
| t_stat ptp_svc (UNIT *uptr) | |
| { | |
| if ((pt_unit[PTP].flags & UNIT_ATT) == 0) /* attached? */ | |
| return IORETURN (ptp_stopioe, SCPE_UNATT); | |
| if (pt_rw) { /* write mode? */ | |
| if (pt_busy) SET_INT (PT); /* if busy, intr */ | |
| pt_busy = 0; } /* not busy */ | |
| if (putc (pt_unit[PTP].buf, pt_unit[PTP].fileref) == EOF) { | |
| perror ("PTP I/O error"); | |
| clearerr (pt_unit[PTP].fileref); | |
| return SCPE_IOERR; } | |
| pt_unit[PTP].pos = pt_unit[PTP].pos + 1; | |
| return SCPE_OK; | |
| } | |
| /* Bootstrap routine */ | |
| #define BOOT_START 0x3E | |
| #define BOOT_LEN (sizeof (boot_rom) / sizeof (unsigned int16)) | |
| static const unsigned int16 boot_rom[] = { | |
| 0xC820, /* START: LHI 2,80 */ | |
| 0x0080, | |
| 0xC830, /* LHI 3,1 */ | |
| 0x0001, | |
| 0xC840, /* LHI 4,CF */ | |
| 0x00CF, | |
| 0xD3A0, /* LB A,78 */ | |
| 0x0078, | |
| 0xDEA0, /* OC A,79 */ | |
| 0x0079, | |
| 0x9DAE, /* LOOP: SSR A,E */ | |
| 0x42F0, /* BTC F,LOOP */ | |
| 0x0052, | |
| 0x9BAE, /* RDR A,E */ | |
| 0x08EE, /* LHR E,E */ | |
| 0x4330, /* BZ LOOP */ | |
| 0x0052, | |
| 0x4300, /* BR STORE */ | |
| 0x006C, | |
| 0x9DAE, /* LOOP1: SSR A,E */ | |
| 0x42F0, /* BTC F,LOOP1 */ | |
| 0x0064, | |
| 0x9BAE, /* RDR A,E */ | |
| 0xD2E2, /* STORE: STB E,0(2) */ | |
| 0x0000, | |
| 0xC120, /* BXLE 2,LOOP1 */ | |
| 0x0064, | |
| 0x4300, /* BR 80 */ | |
| 0x0080, | |
| 0x0395, /* HS PAPER TAPE INPUT */ | |
| 0x039A, /* HS PAPER TAPE OUTPUT */ | |
| 0x0420, /* CARD INPUT TO ASSEMBLER */ | |
| 0x0298 /* TELEPRINTER OUTPUT FOR ASSEMBLER */ | |
| }; | |
| t_stat pt_boot (int32 unitno) | |
| { | |
| int32 i; | |
| extern int32 saved_PC; | |
| for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; | |
| saved_PC = BOOT_START; | |
| return SCPE_OK; | |
| } | |
| /* Reset routine */ | |
| t_stat pt_reset (DEVICE *dptr) | |
| { | |
| sim_cancel (&pt_unit[PTR]); /* deactivate units */ | |
| sim_cancel (&pt_unit[PTP]); | |
| pt_busy = pt_run = pt_slew = pt_rw = 0; | |
| return SCPE_OK; | |
| } | |
| /* Terminal: IO routine */ | |
| int32 tt (int32 op, int32 dat) | |
| { | |
| int32 t, old_tt_rw; | |
| switch (op) { /* case IO op */ | |
| case IO_ADR: /* select */ | |
| break; | |
| case IO_OC: /* command */ | |
| t = CMD_GETINT (dat); /* get enable */ | |
| if (t == CMD_IENB) SET_ENB (TT); /* process */ | |
| else if (t == CMD_IDIS) CLR_ENB (TT); | |
| else if (t == CMD_ICOM) COM_ENB (TT); | |
| t = TT_GETDPX (dat); /* get duplex */ | |
| if (t == TT_FDPX) tt_hdpx = 0; /* process */ | |
| else if (t == TT_HDPX) tt_hdpx = 1; | |
| else if (t == TT_CDPX) tt_hdpx = tt_hdpx ^ 1; | |
| t = TT_GETRW (dat); /* read/write */ | |
| old_tt_rw = tt_rw; | |
| if (t == TT_RD) tt_rw = 0; /* process */ | |
| else if (t == TT_WD) tt_rw = 1; | |
| else if (t == TT_CRW) tt_rw = tt_rw ^ 1; | |
| if (tt_rw == 0) { /* read? */ | |
| if (old_tt_rw != 0) tt_busy = 1; } /* chg? set busy */ | |
| else { if (sim_is_active (&tt_unit[TTO])) tt_busy = 1; | |
| else { if (tt_busy) SET_INT (TT); | |
| tt_busy = 0; } } | |
| break; | |
| case IO_RD: /* read */ | |
| if (tt_rw == 0) tt_busy = 1; | |
| return (tt_unit[TTI].buf & 0xFF); | |
| case IO_WD: /* write */ | |
| tt_unit[TTO].buf = dat & 0xFF; | |
| sim_activate (&tt_unit[TTO], tt_unit[TTO].wait); | |
| if (pt_rw) tt_busy = 1; | |
| break; | |
| case IO_SS: /* status */ | |
| t = tt_busy? STA_BSY: 0; | |
| return t; } | |
| return 0; | |
| } | |
| /* Unit service routines */ | |
| t_stat tti_svc (UNIT *uptr) | |
| { | |
| int32 temp; | |
| sim_activate (&tt_unit[TTI], tt_unit[TTI].wait); /* continue poll */ | |
| if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ | |
| if (tt_rw == 0) { /* read mode? */ | |
| if (tt_busy) SET_INT (TT); /* if busy, intr */ | |
| tt_busy = 0; } /* not busy */ | |
| temp = temp & 0x7F; | |
| if ((tt_unit[TTI].flags & UNIT_UC) && islower (temp)) | |
| temp = toupper (temp); | |
| tt_unit[TTI].buf = temp | 0x80; /* got char */ | |
| tt_unit[TTI].pos = tt_unit[TTI].pos + 1; | |
| if (tt_hdpx) { /* half duplex? */ | |
| sim_putchar (temp); /* write char */ | |
| tt_unit[TTO].pos = tt_unit[TTO].pos + 1; } | |
| return SCPE_OK; | |
| } | |
| t_stat tto_svc (UNIT *uptr) | |
| { | |
| int32 temp; | |
| if (tt_rw) { /* write mode? */ | |
| if (tt_busy) SET_INT (TT); /* if was busy, intr */ | |
| tt_busy = 0; } /* not busy */ | |
| if ((temp = sim_putchar (tt_unit[TTO].buf & 0177)) != SCPE_OK) return temp; | |
| tt_unit[TTO].pos = tt_unit[TTO].pos + 1; | |
| return SCPE_OK; | |
| } | |
| /* Reset routine */ | |
| t_stat tt_reset (DEVICE *dptr) | |
| { | |
| sim_activate (&tt_unit[TTI], tt_unit[TTI].wait); /* activate input */ | |
| sim_cancel (&tt_unit[TTO]); /* cancel output */ | |
| tt_hdpx = tt_rw = 0; | |
| tt_busy = 1; /* no kbd input yet */ | |
| return SCPE_OK; | |
| } |