From 35ef1c8d243f1c5e1b62397be4bddf12171d47a0 Mon Sep 17 00:00:00 2001 From: "Howard M. Harte" Date: Wed, 7 Nov 2012 20:47:26 -0800 Subject: [PATCH] Initial public version of TX-0 for SIMH Integrate TX-0 simulation into SIMH. --- TX-0/resource.h | 20 + TX-0/tx0.ico | Bin 0 -> 2998 bytes TX-0/tx0.rc | 110 ++ TX-0/tx0_cpu.c | 1476 ++++++++++++++++++ TX-0/tx0_defs.h | 93 ++ TX-0/tx0_diag.txt | 16 + TX-0/tx0_dpy.c | 139 ++ TX-0/tx0_stddev.c | 623 ++++++++ TX-0/tx0_sys.c | 439 ++++++ TX-0/tx0_sys_orig.c | 133 ++ display/README | 166 +++ display/build_mingw.bat | 12 + display/display.c | 1053 +++++++++++++ display/display.h | 143 ++ display/gmakefile | 77 + display/smakefile | 75 + display/test.c | 192 +++ display/type340.c | 706 +++++++++ display/vt11.c | 3148 +++++++++++++++++++++++++++++++++++++++ display/vt11.h | 137 ++ display/vtmacs.h | 291 ++++ display/vttest.c | 1301 ++++++++++++++++ display/win32.c | 418 ++++++ display/ws.h | 66 + display/x11.c | 512 +++++++ 25 files changed, 11346 insertions(+) create mode 100644 TX-0/resource.h create mode 100644 TX-0/tx0.ico create mode 100644 TX-0/tx0.rc create mode 100644 TX-0/tx0_cpu.c create mode 100644 TX-0/tx0_defs.h create mode 100644 TX-0/tx0_diag.txt create mode 100644 TX-0/tx0_dpy.c create mode 100644 TX-0/tx0_stddev.c create mode 100644 TX-0/tx0_sys.c create mode 100644 TX-0/tx0_sys_orig.c create mode 100644 display/README create mode 100644 display/build_mingw.bat create mode 100644 display/display.c create mode 100644 display/display.h create mode 100644 display/gmakefile create mode 100644 display/smakefile create mode 100644 display/test.c create mode 100644 display/type340.c create mode 100644 display/vt11.c create mode 100644 display/vt11.h create mode 100644 display/vtmacs.h create mode 100644 display/vttest.c create mode 100644 display/win32.c create mode 100644 display/ws.h create mode 100644 display/x11.c diff --git a/TX-0/resource.h b/TX-0/resource.h new file mode 100644 index 00000000..8ab407b6 --- /dev/null +++ b/TX-0/resource.h @@ -0,0 +1,20 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by tx0.rc +// +#define HTC_VERS_MIN 0 +#define HTC_VERS_REV 0 +#define HTC_VERS_MAJ 1 +#define IDI_ICON1 106 +#define HTC_VERS_BUILD 1746 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/TX-0/tx0.ico b/TX-0/tx0.ico new file mode 100644 index 0000000000000000000000000000000000000000..627c7e52e0c0f5786328c104e31bf3965290518d GIT binary patch literal 2998 zcmeH}ze{9S6vw~npu;e%v9S{3T7+2N$|{S!u#L6d-7c+7Aa4;&8wd!u+ad``k;cDZ zAdn*7TAK*7nPP)1xU!9q#GB7KcitPv{Hj8VkruS5&CPct_4L!C z^d486n@sBKtG@JTNqXOO$P;7%KOgwE!=g>h*f+_xlRdP&lF$?Fa#I^42}dtfFZyTUTlG7O|a@Q0zmP+%x9kQzY`LxG{dPyi^@&m=1Z7MvnEBKrtO@!aMU^;lo% z;NU=qhle^oKGw;}iO$Z>bb5NKi;D|gUS4W4nds{3N;fw*y1l*Co1cq5fANC%xS_lD z4%k9I-&tPU7A?<@hx>W!GQkftwjB&wrI!E3-o@LGV;j}Q&(fQ*bNHw2l&xItT5EUp zJNt5XdmW!e&g|XQoPFA&nvbrte_;2n-M8Z~-8z|7sX;Zo?HoY&>;&1lnz1{EIlE;w z|ElyPzq5O^C!M#O%Ja`V)ObtlkC#n2RPD?Y< AC 0) + shr --- --- --- 100 --- --- 1.6 Shift AC contents right one binary position (AC 0 unchanged) + mbl --- --- --- 01x --- --- 1.4 Transfer MBR contents to LR + xmb --- --- --- 0x1 --- --- 1.2 Transfer XR contents to MBR + com --- --- --- --- 1-- --- 1.2 Compliment AC + pad --- --- --- --- -1- --- 1.5 Parital add MBR to AC (for each MBR one, complement the correp AC bit) + cry --- --- --- --- --1 --- 1.7 A carry digit is a one if in the next least sigmificant digit, + either ac=0 and mbr=1 or ac=1 and carry digit=1. The carry digits + so determined are partial added to the AC by cry. pad and cry used + together give a full one's complement addition of C(MBR) to C(AC) + anb --- --- --- --- --- 111 1.2-2 And LR and MBR + orb --- --- --- --- --- 101 1.3 Or LR into MBR + lmb --- --- --- --- --- 01x 1.4 Tranfer LR contents to MBR + mbx --- --- --- --- --- 0x1 1.8 Transfer MBR contents to XR +*/ +#include "tx0_defs.h" + +#define OPR_CLA 0100000 /* 0.8 */ +#define OPR_AMB 0040000 /* 0.7 */ + +#define OPR_SHF_MASK 0000700 /* 1.6 */ +#define OPR_CYR 0000600 +#define OPR_SHR 0000400 + +#define OPR_MBL_MASK 0000600 /* 1.4 */ +#define OPR_MBL 0000200 +#define OPR_XMB_MASK 0000500 /* 1.2 */ +#define OPR_XMB 0000100 + +#define OPR_COM 0000040 /* 1.2 */ +#define OPR_PAD 0000020 /* 1.5 */ +#define OPR_CRY 0000010 /* 1.7 */ + +#define OPR_LOG_MASK 0000007 /* Logical operation mask */ +#define OPR_ANB 0000007 /* 1.2-2 */ +#define OPR_ORB 0000005 /* 1.3 */ + +#define OPR_LMB_MASK 0000006 /* 1.4 */ +#define OPR_LMB 0000002 +#define OPR_MBX_MASK 0000005 /* 1.8 */ +#define OPR_MBX 0000001 + +/* + IN OUT GROUP + ------------ + nop --- -00 000 --- --- --- NOP + tac --- -00 001 --- --- --- 1.1 + tbr --- -00 010 --- --- --- 1.2 + pen --- -00 011 --- --- --- 1.1 + sel --- -00 100 --- --- --- + spare--- -00 101 --- --- --- + rpf --- -00 110 --- --- --- 1.2 + spf --- -00 111 --- --- --- 1.6 + exN --- -01 nnn --- --- --- IOS + cpy --- -10 000 --- --- --- IOS + r1l --- -10 001 --- --- --- IOS + dis --- -10 010 --- --- --- IOS + r3l --- -10 011 --- --- --- IOS + prt --- -10 100 --- --- --- IOS + spare--- -10 101 --- --- --- + p6h --- -10 110 --- --- --- IOS + p7h --- -10 111 --- --- --- IOS + hlt --- -11 000 --- --- --- 1.8 + cll --- -11 001 --- --- --- 0.6 + clr --- -11 010 --- --- --- 0.6 +*/ +#define IOS_MASK 0037000 +#define IOS_EX_MASK 0030000 +#define IOS_NOP 0000000 +#define IOS_TAC 0001000 +#define IOS_TBR 0002000 +#define IOS_PEN 0003000 +#define IOS_SEL 0004000 +#define IOS_RPF 0006000 +#define IOS_SPF 0007000 +#define IOS_CPY 0020000 +#define IOS_R1L 0021000 +#define IOS_DIS 0022000 +#define IOS_R3L 0023000 +#define IOS_PRT 0024000 +#define IOS_P6H 0026000 +#define IOS_P7H 0027000 +#define IOS_HLT 0030000 +#define IOS_CLL 0031000 +#define IOS_CLR 0032000 + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC +#define UNIT_V_MSIZE (UNIT_V_UF + 4) /* dummy mask */ +#define UNIT_V_EXT (UNIT_V_UF + 2) +#define UNIT_EXT_INST (1 << UNIT_V_EXT) +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +#define HIST_PC 0x40000000 +#define HIST_V_SHF 18 +#define HIST_MIN 64 +#define HIST_MAX 65536 + +#define TRACE_PRINT(level, args) if(cpu_dev.dctrl & level) { \ + printf args; \ + } +typedef struct { + uint32 pc; + uint32 ir; + uint32 ovac; + uint32 pfio; + uint32 ea; + uint32 opnd; +} InstHistory; + +int32 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 AC = 0; /* AC */ +int32 IR = 0; /* IR */ +int32 PC = 0; /* PC */ +int32 MAR = 0; /* MAR */ +int32 XR = 0; /* XR (index register) */ +int32 MBR = 0; /* MBR */ +int32 LR = 0; /* LR (Live Register) */ +int32 OV = 0; /* overflow */ +int32 TBR = 0; /* sense switches */ +int32 PF = 0; /* program flags */ +int32 TAC = 0; /* Toggle Switch Accumulator */ +int32 iosta = 0; /* status reg */ +int32 ios = 0; /* I/O Stop */ +int32 ch = 0; /* Chime Alarm */ +int32 LP = 0; /* Light Pen / Light Gun flops */ +int32 mode_tst = 1; /* Test Mode Flip-flop */ +int32 mode_rdin = 1; /* Read-In Mode Flip-flop */ + +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* inst history */ + +int32 fpc_MA; /* shadow ma for FPC access */ +int32 fpc_OP; /* shadow op for FPC access */ + +int32 addr_mask = YMASK; + +extern UNIT *sim_clock_queue; +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +int32 cpu_get_mode (void); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_ext (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_noext (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat Read (void); +t_stat Write (void); + +extern int32 petr (int32 inst, int32 dev, int32 dat); +extern int32 ptp (int32 inst, int32 dev, int32 dat); +extern int32 tti (int32 inst, int32 dev, int32 dat); +extern int32 tto (int32 inst, int32 dev, int32 dat); +extern int32 lpt (int32 inst, int32 dev, int32 dat); +extern int32 dt (int32 inst, int32 dev, int32 dat); +extern int32 drm (int32 inst, int32 dev, int32 dat); +#ifdef USE_DISPLAY +extern int32 dpy (int32 ac); +#endif + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX | UNIT_BINK | UNIT_EXT_INST | UNIT_MODE_READIN, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, PC, ASIZE) }, + { ORDATA (AC, AC, 18) }, + { ORDATA (IR, IR, 5) }, + { ORDATA (MAR, MAR, 16) }, + { ORDATA (XR, XR, 14) }, + { ORDATA (MBR, MBR, 18) }, + { ORDATA (LR, LR, 18) }, + { ORDATA (TAC, TAC, 18) }, + { ORDATA (PF, PF, 18) }, + { BRDATA (PCQ, pcq, 8, ASIZE, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { FLDATA (IOS, ios, 0) }, /* In Out Stop */ + { FLDATA (CH, ch, 0) }, /* Chime Alarm */ + { ORDATA (LP, LP, 2) }, /* Light Pen */ + { FLDATA (R, mode_rdin, 0), REG_HRO }, /* Mode "R" (Read In) Flip-Flop */ + { FLDATA (T, mode_tst, 0), REG_HRO }, /* Mode "T" (Test) Flip-Flop */ + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_EXT_INST, 0, "standard CPU", "TX0STD", &cpu_set_noext }, + { UNIT_EXT_INST, UNIT_EXT_INST, "Extended Instruction Set", "TX0EXT", &cpu_set_ext }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, + { UNIT_MODE, 0, "NORMAL", "NORMAL", &cpu_set_mode }, + { UNIT_MODE, UNIT_MODE_TEST, "TEST", "TEST", &cpu_set_mode }, + { UNIT_MODE, UNIT_MODE_READIN, "READIN", "READIN", &cpu_set_mode }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +/* Debug flags */ +#define ERROR_MSG (1 << 0) +#define TRACE_MSG (1 << 1) +#define STO_MSG (1 << 2) +#define ADD_MSG (1 << 3) +#define TRN_MSG (1 << 4) +#define ORD_MSG (1 << 5) +#define IOS_MSG (1 << 6) +#define READIN_MSG (1 << 7) +#define VERBOSE_MSG (1 << 8) +#define COUNTERS_MSG (1 << 9) + +/* Debug Flags */ +static DEBTAB cpu_dt[] = { + { "ERROR", ERROR_MSG }, + { "TRACE", TRACE_MSG }, + { "STO", STO_MSG }, + { "ADD", ADD_MSG }, + { "TRN", TRN_MSG }, + { "ORD", ORD_MSG }, + { "IOS", IOS_MSG }, + { "READIN", READIN_MSG }, + { "VERBOSE",VERBOSE_MSG }, + { "COUNTERS",COUNTERS_MSG }, + { NULL, 0 } +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, ASIZE, 1, 8, 18, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + NULL, DEV_DEBUG, ERROR_MSG, + cpu_dt, NULL + }; + +int32 compute_index (int32 y, int32 XR) +{ + int32 sum; + + y &= YMASK; /* force 13-bit (0 sign) */ + XR &= 037777; /* force 14-bit */ + + sum = y + XR; + + if (sum > 037777) { /* Carry from bit 4 into bit 17. */ + sum += 1; + } + + sum &= YMASK; /* truncate to 13-bit */ + + return (sum); +} + +/* CPU Instruction usage counters */ +typedef struct { +/* Store group */ + int32 sto, stx, sxa, ado, slr, slx, stz; +/* Add group */ + int32 add, adx, ldx, aux, llr, llx, lda, lax; +/* TRN Group */ + int32 trn, tze, tsx, tix, tra, trx, tlv; +/* OPR Group */ + int32 cla, amb, cyr, shr, mbl, xmb, com, pad, cry, anb, orb, lmb, mbx; +} INST_CTRS; + +INST_CTRS inst_ctr; + + +void tx0_dump_regs(char *desc) +{ + TRACE_PRINT(TRACE_MSG, ("%s: AC=%06o, MAR=%05o, MBR=%06o, LR=%06o, XR=%05o\n", desc, AC, MAR, MBR, LR, XR)); + + /* Check regs sanity */ + if (AC > DMASK) { + printf("Error: AC > DMASK\n"); + } + if (MBR > DMASK) { + printf("Error: MBR > DMASK\n"); + } + if (LR > DMASK) { + printf("Error: LR > DMASK\n"); + } + if (!MEM_ADDR_OK(MAR)) { + printf("Error: MAR > %06o\n", MEMSIZE); + } + +} + +t_stat sim_opr_orig(int32 op); + +t_stat sim_instr (void) +{ + extern int32 sim_interval; + int32 IR, op, inst_class, y; + int32 tempLR; /* LR temporary storage in case both LMB and MBL are set (swap LR<->MBR) */ + t_stat reason; + + /* Clear Instruction counters */ + inst_ctr.sto = inst_ctr.stx = inst_ctr.sxa = inst_ctr.ado = inst_ctr.slr = inst_ctr.slx = inst_ctr.stz = 0; + inst_ctr.add = inst_ctr.adx = inst_ctr.ldx = inst_ctr.aux = inst_ctr.llr = inst_ctr.llx = inst_ctr.lda = inst_ctr.lax = 0; + inst_ctr.trn = inst_ctr.trn = inst_ctr.tze = inst_ctr.tsx = inst_ctr.tix = inst_ctr.tra = inst_ctr.trx = inst_ctr.tlv = 0; + inst_ctr.cla = inst_ctr.amb = inst_ctr.cyr = inst_ctr.shr = inst_ctr.mbl = inst_ctr.xmb = inst_ctr.com = inst_ctr.pad = inst_ctr.cry = inst_ctr.anb = inst_ctr.orb = inst_ctr.lmb = inst_ctr.mbx = 0; + + #define INCR_ADDR(x) ((x+=1) & (MEMSIZE-1)) + + /* Main instruction fetch/decode loop: check events */ + + reason = 0; + while (reason == 0) { /* loop until halted */ + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + } + + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + if (ios) { + TRACE_PRINT(ERROR_MSG, ("I/O Stop - Waiting...\n")); + continue; + } + + /* Handle Instruction Execution in TEST and READIN modes */ + if (mode_tst) { /* Test Mode / Readin mode */ + if (mode_rdin) { /* Readin Mode */ + reason = SCPE_OK; /* Default is to continue reading, and transfer control when done. */ + AC = petr(3,0,0); /* Read three chars from tape into AC */ + MAR = AC & AMASK; /* Set memory address */ + IR = AC >> 16; + + if (!MEM_ADDR_OK(MAR)) { + TRACE_PRINT(ERROR_MSG, ("READIN: Tape address out of range.\n")); + reason = SCPE_FMT; + } + + switch (IR) { + case 00: /* Storage (sto x) */ + case 03: /* Storage (opr x) */ + MBR = petr(3,0,0); /* Read three characters from tape. */ + TRACE_PRINT(READIN_MSG, ("READIN: sto @%06o = %06o\n", MAR, MBR)); + Write(); + break; + case 02: /* Transfer Control (trn x) Start Execution */ + PC = MAR; + reason = SCPE_OK; /* let SIMH start execution. */ + TRACE_PRINT(READIN_MSG, ("READIN: trn %06o (Start Execution)\n", PC)); + reason = cpu_set_mode(&cpu_unit, 0, NULL, NULL); + break; + case 01: /* Transfer (add x) - Halt */ + PC = MAR; + reason = SCPE_STOP; /* let SIMH halt. */ + TRACE_PRINT(READIN_MSG, ("READIN: add %06o (Halt)\n", PC)); + reason = cpu_set_mode(&cpu_unit, 0, NULL, NULL); + break; + default: + reason = SCPE_IERR; + break; + } + } else if (mode_tst) { /* Test mode not implemented yet. */ + TRACE_PRINT(ERROR_MSG, ("TEST Mode not implemented.\n")); + reason = SCPE_STOP; + + } else { + TRACE_PRINT(ERROR_MSG, ("Invalid CPU mode.\n")); + reason = SCPE_IERR; + } + continue; /* Proceed with next instruction */ + } + + /* Fetch, decode instruction in NORMAL mode */ + MAR = PC; + if (Read ()) break; /* fetch inst */ + + IR = (MBR >> 13); /* save in IR */ + inst_class = IR >> 3; + op = MBR & AMASK; + y = MBR & YMASK; + sim_interval = sim_interval - 1; + + if ((cpu_unit.flags & UNIT_EXT_INST) == 0) { /* Original instruction set */ + IR &= 030; + MAR = MBR & AMASK; /* 16-bit address field */ + } else { + MAR = MBR & YMASK; /* 13-bit address field */ + } + + if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) hst_p = 0; + hst[hst_p].pc = MAR | HIST_PC; /* save state */ + hst[hst_p].ir = IR; + hst[hst_p].ovac = (OV << HIST_V_SHF) | AC; + } + + PC = INCR_ADDR (PC); /* increment PC */ + +#ifdef USE_FPC + fpc_OP = op; /* shadow opcode for FPC */ +#endif + + tx0_dump_regs("START"); + + switch (inst_class) { /* decode IR<0:1> */ + + /* Logical, load, store instructions */ + case 00: /* sto x */ + switch (IR & 07) { + case 0: /* sto */ + MBR = AC; + Write(); + inst_ctr.sto++; + break; + case 1: /* stx */ + MBR = AC; + MAR = compute_index(y, XR); + Write(); + inst_ctr.stx++; + break; + case 2: /* sxa */ + { + int32 temp = M[MAR]; + temp &= 0760000; + temp |= (XR & YMASK); + MBR = temp; + Write(); + } + inst_ctr.sxa++; + break; + case 3: /* ado */ + { + int32 temp = M[MAR]; + temp += 1; /* add 1 */ + if (temp > DMASK) { /* Overflow, */ + temp += 1; /* propagate carry from bit 0 to bit 17. */ + } + temp &= DMASK; + MBR = temp; + AC = temp; + Write(); + } + inst_ctr.ado++; + break; + case 4: /* slr */ + MBR = LR; + Write(); + inst_ctr.slr++; + break; + case 5: /* slx */ + MAR = compute_index(y, XR); + MBR = LR; + Write(); + inst_ctr.slx++; + break; + case 6: /* stz */ + MBR = 0; + Write(); + inst_ctr.stz++; + break; + case 7: /* no-op */ + break; + } + break; + + case 01: /* add x */ + switch (IR & 07) { + case 0: /* add */ + Read(); + AC = AC + MBR; + if (AC > DMASK) { + AC += 1; + } else; + AC &= DMASK; + inst_ctr.add++; + break; + case 1: /* adx */ + MAR = compute_index(y, XR); + Read(); + AC = AC + MBR; + if (AC > DMASK) { + AC += 1; + } else; + AC &= DMASK; + inst_ctr.adx++; + break; + case 2: /* ldx */ + Read(); + XR = MBR & YMASK; /* load XR[5:17] from C(y[5:17]) */ + XR |= ((MBR & SIGN) >> 4); /* Load XR[4] from C(y[0]) */ + inst_ctr.ldx++; + break; + case 3: /* aux (Augment Index) */ + { + uint32 newY = (y & 0017777) | ((y & SIGN) >> 4); + TRACE_PRINT(ADD_MSG, ("[%06o] AUX: y=%05o, XR=%05o = ", PC-1, newY, XR)); + XR = XR + newY; + TRACE_PRINT(ADD_MSG, ("%05o\n", XR)); + break; + } + inst_ctr.aux++; + case 4: /* llr (Load Live Register) */ + Read(); + LR = MBR; + inst_ctr.llr++; + break; + case 5: /* llx (Load Live Register, Indexed) */ + MAR = compute_index(y, XR); + Read(); + LR = MBR; + inst_ctr.llx++; + break; + case 6: /* lda (Load Accumulator) */ + Read(); + AC = MBR; + inst_ctr.lda++; + break; + case 7: /* lax (Load Accumulator, Indexed) */ + MAR = compute_index(y, XR); + Read(); + AC = MBR; + inst_ctr.lax++; + break; + } + break; + + case 02: /* trn x */ + switch (IR & 07) { + case 0: /* trn (Transfer on Negative AC) */ + if (AC & SIGN) { + TRACE_PRINT(TRN_MSG, ("[%06o] TRN: Transfer taken: PC=%06o\n", PC-1, y)); + PC = MAR; + } + inst_ctr.trn++; + break; + case 1: /* tze (Transfer on +/- Zero) */ + if ((AC == 0777777) || (AC == 0000000)) { + TRACE_PRINT(TRN_MSG, ("[%06o] TZE: Transfer taken: PC=%06o\n", PC-1, y)); + PC = y; + } + inst_ctr.tze++; + break; + case 2: /* tsx (Transfer and set Index) */ + XR = PC & 0017777; /* XR[4] = 0; */ + PC = y; + TRACE_PRINT(TRN_MSG, ("[%06o] TSX: PC=%06o, XR=%05o\n", PC, XR)); + inst_ctr.tsx++; + break; + case 3: /* tix (Transfer and Index) */ + TRACE_PRINT(TRN_MSG, ("[%06o] TIX: XR=%05o\n", PC-1, XR)); + if ((XR == 037777) || (XR == 000000)) { /* +/- 0, take next instruction */ + TRACE_PRINT(TRN_MSG, ("+/- 0, transfer not taken.\n")); + } else { /* Not +/- 0 */ + if (XR & 0020000) { /* XR[4] == 1 */ + TRACE_PRINT(TRN_MSG, ("XR is negative, transfer taken,")); + XR ++; + } else { /* XR[4] = 0 */ + TRACE_PRINT(TRN_MSG, ("XR is positive, transfer taken,")); + XR --; + } + PC = y; + XR &= 037777; + TRACE_PRINT(TRN_MSG, (" PC=%06o, XR=%05o\n", PC, XR)); + + } + inst_ctr.tix++; + break; + case 4: /* tra (Unconditional Transfer) */ + TRACE_PRINT(TRN_MSG, ("[%06o] TRA: Transfer taken: PC=%06o\n", PC-1, y)); + PC = y; + inst_ctr.tra++; + break; + case 5: /* trx */ + { + int32 newPC; + newPC = compute_index(y, XR); + TRACE_PRINT(TRN_MSG, ("[%06o] TRA: Transfer taken: PC=%06o\n", PC-1, newPC)); + PC = newPC; + } + inst_ctr.trx++; + break; + case 6: /* tlv (Transfer on External Level) */ + TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: Implement TLV\n", PC-1)); + inst_ctr.tlv++; + break; + case 7: /* no-op */ + break; + } + break; + + case 03: /* opr x */ + if ((cpu_unit.flags & UNIT_EXT_INST) == 0) { /* Original instruction set */ + reason = sim_opr_orig(op); + break; + } + + /* I can't find this mentioned in the TX-0 Documentation, but for the + * lro and xro instructions, this must be needed. + */ + MBR = 0; + +/* Cycle 0 */ + if (op & OPR_AMB) { /* 0.7 */ + inst_ctr.amb++; + MBR = AC; + TRACE_PRINT(ORD_MSG, ("[%06o]: AMB: MBR=%06o\n", PC-1, MBR)); + } + + if (op & OPR_CLA) { /* 0.8 */ + inst_ctr.cla++; + AC = 0; + TRACE_PRINT(ORD_MSG, ("[%06o]: CLA: AC=%06o\n", PC-1, AC)); + } + +/* IOS - In / Out Stop */ + /* Check TTI for character. If so, put in LR and set LR bit 0. */ + if (iosta & IOS_TTI) { + int32 rbuf; + rbuf = tti(0,0,0); + TRACE_PRINT(IOS_MSG, ("TTI: character received=%03o\n", rbuf &077)); + LR &= 0266666; /* Clear bits 0,2,5,8,...,17 */ + + LR |= SIGN; /* Set bit 0, character available. */ + LR |= ((rbuf & 001) >> 0) << 15;/* bit 2 */ + LR |= ((rbuf & 002) >> 1) << 12;/* bit 5 */ + LR |= ((rbuf & 004) >> 2) << 9; /* bit 8 */ + LR |= ((rbuf & 010) >> 3) << 6; /* bit 11 */ + LR |= ((rbuf & 020) >> 4) << 3; /* bit 14 */ + LR |= ((rbuf & 040) >> 5) << 0; /* bit 17 */ + } + + switch(op & IOS_MASK) { + case IOS_NOP: + break; + case IOS_TAC: + TRACE_PRINT(IOS_MSG, ("[%06o] TAC %06o\n", PC-1, TAC)); + AC |= TAC; + break; + case IOS_TBR: + TRACE_PRINT(IOS_MSG, ("[%06o] TBR %06o\n", PC-1, TBR)); + MBR |= TBR; + break; + case IOS_PEN: + TRACE_PRINT(IOS_MSG, ("[%06o] Light Pen %01o\n", PC-1, LP)); + AC &= AMASK; + AC |= (LP & 1) << 17; + AC |= (LP & 2) << 16; + AC &= DMASK; + break; + case IOS_SEL: + { /* These are used for Magtape control. + Magtape is compatible with IBM 709. Maybe the SIMH 7090 magtape can be leveraged. */ + int32 CLRA = (op && 0100000); + int32 BINDEC = (op && 020); + int32 device = op & 03; + int32 tape_ord = (op >> 2) & 03; + char *tape_cmd[] = {"Backspace Tape", "Read/Select Tape", "Rewind Tape", "Write/Select Tape" }; + + TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: SEL (magtape)\n", PC-1)); + printf("Device %d: CLRA=%d, BINDEC=%d: %s\n", device, CLRA, BINDEC, tape_cmd[tape_ord]); + } + break; + case IOS_RPF: /* These are used for Magtape control. */ + TRACE_PRINT(IOS_MSG, ("[%06o] RPF %06o\n", PC-1, PF)); + MBR |= PF; + break; + case IOS_SPF: /* These are used for Magtape control. */ + TRACE_PRINT(IOS_MSG, ("[%06o] SPF %06o\n", PC-1, MBR)); + PF = MBR; + break; + case IOS_CPY: /* These are used for Magtape control. */ + TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: CPY\n", PC-1)); + break; + case IOS_R1L: + AC &= 0333333; /* Clear bits 0,3,6,9,12,15 */ + AC |= petr(1, 0, 0); /* Read one line from PETR */ + break; + case IOS_DIS: +#ifdef USE_DISPLAY + LP = dpy (AC); /* Display point on the CRT */ +#endif /* USE_DISPLAY */ + break; + case IOS_R3L: + AC = petr(3, 0, 0); /* Read three lines from PETR */ + break; + case IOS_PRT: + { + uint32 tmpAC = 0; + tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */ + tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */ + tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */ + tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */ + tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */ + tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */ + tto (0, 0, tmpAC & 077); /* Print one character on TTO */ + } + break; + case IOS_P6H: + case IOS_P7H: + { + uint32 tmpAC = 0; + tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */ + tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */ + tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */ + tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */ + tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */ + tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */ + tmpAC &= 0077; + if ((op & IOS_MASK) == IOS_P7H) { + tmpAC |= 0100; /* Punch 7th hole. */ + TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 7 holes\n", PC-1)); + } else { + TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 6 holes\n", PC-1)); + } + ptp (0, 0, tmpAC); /* Punch character on PTP */ + } + break; + case IOS_HLT: + TRACE_PRINT(IOS_MSG, ("[%06o] HALT Instruction\n", PC-1)); + reason = STOP_HALT; + break; + case IOS_CLL: + AC &= 0000777; + break; + case IOS_CLR: + AC &= 0777000; + break; + default: /* Could be ex0-ex7, handle them here. */ + if ((op & IOS_EX_MASK) == 0010000) { + TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: EX%o\n", PC-1, (op >> 9) & 07)); + } + break; + } + +/* Cycle 1 */ + if (op & OPR_COM) { /* 1.2 */ + AC = ~AC; + AC &= DMASK; + TRACE_PRINT(ORD_MSG, ("[%06o]: COM: AC=%06o\n", PC-1, AC)); + inst_ctr.com++; + } + + if ((op & OPR_XMB_MASK) == OPR_XMB) { /* 1.2 XR[5:17] -> MBR[5:17], XR[4] -> MBR[0:4] */ + int32 bit14 = (XR >> 13) & 1; + MBR = XR & YMASK; /* XR[5:17] -> MBR[5:17] */ + MBR |= (bit14 << 17); /* XR[4] -> MBR[0] */ + MBR |= (bit14 << 16); /* XR[4] -> MBR[1] */ + MBR |= (bit14 << 15); /* XR[4] -> MBR[2] */ + MBR |= (bit14 << 14); /* XR[4] -> MBR[3] */ + MBR |= (bit14 << 13); /* XR[4] -> MBR[4] */ + + TRACE_PRINT(ORD_MSG, ("[%06o]: XMB: XR=%05o, MBR=%06o\n", PC-1, XR, MBR)); + inst_ctr.xmb++; + } + + if ((op & OPR_LOG_MASK) == OPR_ANB) { /* 1.2-2 */ + MBR &= LR; + TRACE_PRINT(ORD_MSG, ("[%06o]: ANB: MBR=%06o\n", PC-1, MBR)); + inst_ctr.anb++; + } + + if ((op & OPR_LOG_MASK) == OPR_ORB) { /* 1.3 */ + MBR |= LR; + TRACE_PRINT(ORD_MSG, ("[%06o]: ORB: MBR=%06o\n", PC-1, MBR)); + inst_ctr.orb++; + } + + tempLR = LR; /* LR temporary storage in case both LMB and MBL are set (swap LR<->MBR) */ + if ((op & OPR_MBL_MASK) == OPR_MBL) { /* 1.4 */ + LR = MBR; + TRACE_PRINT(ORD_MSG, ("[%06o]: MBL: LR=%06o, prev LR=%06o\n", PC-1, LR, tempLR)); + inst_ctr.mbl++; + } + + if ((op & OPR_LMB_MASK) == OPR_LMB) { /* 1.4 */ + MBR = tempLR; + TRACE_PRINT(ORD_MSG, ("[%06o]: LMB: LR=%06o, MBR=%06o\n", PC-1, LR, MBR)); + inst_ctr.lmb++; + } + + if (op & OPR_PAD) { /* 1.5 Partial Add (XOR): AC = MBR ^ AC */ + if (op & OPR_CRY) { /* 1.7 */ + TRACE_PRINT(ORD_MSG, ("[%06o] PAD+CRY: AC=%06o, MBR=%06o = ", PC-1, AC, MBR)); + AC = AC + MBR; + if (AC > DMASK) { + AC += 1; + } else; + AC &= DMASK; + TRACE_PRINT(ORD_MSG, ("%06o\n", AC)); + } else { + TRACE_PRINT(ORD_MSG, ("[%06o] PAD: AC=%06o, MBR=%06o\n", PC-1, AC, MBR)); + AC = AC ^ MBR; + AC &= DMASK; + TRACE_PRINT(ORD_MSG, ("[%06o] PAD: Check: AC=%06o\n", PC-1, AC)); + } + inst_ctr.pad++; + } + + if ((op & OPR_SHF_MASK) == OPR_CYR) { /* 1.6 */ + int32 bit17; + bit17 = (AC & 1) << 17; + AC >>= 1; + AC |= bit17; + TRACE_PRINT(ORD_MSG, ("[%06o]: CYR: AC=%06o\n", PC-1, AC)); + inst_ctr.cyr++; + } + + if ((op & OPR_SHF_MASK) == OPR_SHR) { /* 1.6 Shift AC Right, preserve bit 0. */ + int32 bit0; + bit0 = AC & 0400000; + AC = AC >> 1; + AC |= bit0; + TRACE_PRINT(ORD_MSG, ("[%06o]: SHR: AC=%06o\n", PC-1, AC)); + inst_ctr.shr++; + } + + if (op & OPR_CRY) { /* 1.7 */ + if (op & OPR_PAD) { + } else { + TRACE_PRINT(ERROR_MSG, ("[%06o] CRY: TODO: AC=%06o\n", PC-1, AC)); + inst_ctr.cry++; + } + } + + if ((op & OPR_MBX_MASK) == OPR_MBX) { /* 1.8 MBR[5:17] -> XR[5:17], MBR[0] -> XR[4] */ + int32 tempXR; + tempXR = MBR & YMASK; + tempXR |= (((MBR >> 17) & 1) << 13); + + XR = tempXR; + TRACE_PRINT(ORD_MSG, ("[%06o]: MBX: MBR=%06o, XR=%06o\n", PC-1, MBR, XR)); + inst_ctr.mbx++; + } + } + + tx0_dump_regs("END"); + +#ifdef USE_FPC + fpc_MA = MAR; /* shadow MAR for FPC */ +#endif + + } /* end while */ + pcq_r->qptr = pcq_p; /* update pc q ptr */ + + TRACE_PRINT(COUNTERS_MSG, ("Instruction Counters\nSTO=%d, STX=%d, SXA=%d, ADO=%d, SLR=%d, SLX=%d, STZ=%d\n", + inst_ctr.sto, inst_ctr.stx, inst_ctr.sxa, inst_ctr.ado, inst_ctr.slr, inst_ctr.slx, inst_ctr.stz)); + TRACE_PRINT(COUNTERS_MSG, ("ADD=%d, ADX=%d, LDX=%d, AUX=%d, LLR=%d, LLX=%d, LDA=%d, LAX=%d\n", + inst_ctr.add, inst_ctr.adx, inst_ctr.ldx, inst_ctr.aux, inst_ctr.llr, inst_ctr.llx, inst_ctr.lda, inst_ctr.lax)); + TRACE_PRINT(COUNTERS_MSG, ("TRN=%d, TZE=%d, TSX=%d, TIX=%d, TRA=%d, TRX=%d, TLV=%d\n", + inst_ctr.trn, inst_ctr.tze, inst_ctr.tsx, inst_ctr.tix, inst_ctr.tra, inst_ctr.trx, inst_ctr.tlv)); + TRACE_PRINT(COUNTERS_MSG, ("CLA=%d, AMB=%d, CYR=%d, SHR=%d, MBL=%d, XMB=%d, COM=%d, PAD=%d, CRY=%d, ANB=%d, ORB=%d, LMB=%d, MBX=%d\n", + inst_ctr.cla = inst_ctr.amb, inst_ctr.cyr, inst_ctr.shr, inst_ctr.mbl, inst_ctr.xmb, inst_ctr.com, inst_ctr.pad, inst_ctr.cry, inst_ctr.anb, inst_ctr.orb, inst_ctr.lmb, inst_ctr.mbx)); + + return reason; +} + +/* Read and write memory */ +t_stat Read (void) +{ + MAR &= (MEMSIZE - 1); + MBR = M[MAR]; + MBR &= DMASK; + return SCPE_OK; +} + +t_stat Write (void) +{ + MAR &= (MEMSIZE - 1); + MBR &= DMASK; + M[MAR] = MBR; + return SCPE_OK; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ + ios = 0; + PF = 0; + MAR = 0; + MBR = 0; + pcq_r = find_reg ("PCQ", NULL, dptr); + + if (pcq_r) { + pcq_r->qptr = 0; + } else { + return SCPE_IERR; + } + + sim_brk_types = sim_brk_dflt = SWMASK ('E'); + + return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MEMSIZE) return SCPE_NXM; + if (vptr != NULL) *vptr = M[addr] & DMASK; + + return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MEMSIZE) return SCPE_NXM; + + M[addr] = val & DMASK; + + return SCPE_OK; +} + +/* Change memory size */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + int32 mc = 0; + uint32 i; + + if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; + for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; + if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; + MEMSIZE = val; + for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; + return SCPE_OK; +} + +/* Change CPU Mode (Normal, Test, Readin) */ + +t_stat cpu_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + if (val == UNIT_MODE_TEST) { + mode_tst = 1; + mode_rdin = 0; + } else if (val == UNIT_MODE_READIN) { + mode_tst = 1; + mode_rdin = 1; + } else { /* Normal Mode */ + mode_tst = 0; + mode_rdin = 0; + } + + return SCPE_OK; +} + + +/* Set TX-0 with Extended Instruction Set */ + +t_stat cpu_set_ext (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + printf("Set CPU Extended Mode\n"); + return SCPE_OK; +} + +t_stat cpu_set_noext (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + printf("Set CPU Non-Extended Mode\n"); + return SCPE_OK; +} + +int32 cpu_get_mode (void) +{ + return (cpu_unit.flags & UNIT_EXT_INST); +} + + + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 ov, pf, op, k, di, lnt; +char *cptr = (char *) desc; +t_stat r; +t_value sim_eval; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC OV AC IO PF EA IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + ov = (h->ovac >> HIST_V_SHF) & 1; /* overflow */ + pf = 0; + op = ((h->ir >> 13) & 037); /* get opcode */ + fprintf (st, "%06o %o %06o %06o %03o ", + h->pc & AMASK, ov, h->ovac & DMASK, h->pfio & DMASK, pf); + if ((op < 032) && (op != 007)) /* mem ref instr */ + fprintf (st, "%06o ", h->ea); + else fprintf (st, " "); + sim_eval = h->ir; + if ((fprint_sym (st, h->pc & AMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "(undefined) %06o", h->ir); + else if (op < 030) /* mem ref instr */ + fprintf (st, " [%06o]", h->opnd); + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} + +/* set "test switches"; from display code */ +void +cpu_set_switches(unsigned long bits) +{ + /* just what we want; smaller CPUs might want to shift down? */ + TAC = bits; +} + +unsigned long +cpu_get_switches(void) +{ + return TAC; +} + +t_stat sim_load(FILE *fileref, char *cptr, char *fnam, int32 flag) { + uint32 cnt = 0, word; + t_addr j, lo, hi; + char *result; + + if (flag) { /* Dump to file. */ + result = get_range(NULL, cptr, &lo, &hi, 8, 0xFFFF, 0); + if (result == NULL) return SCPE_ARG; + + for (j = lo; j <= hi; j++) { + if (_putw(j, fileref)== EOF) return SCPE_IOERR; + if (_putw(M[j], fileref) == EOF) return SCPE_IOERR; + } + } else { + lo = strtotv(cptr, &result, 8) & 0xFFFF; + for (j = lo; !feof(fileref); j++) { + if ((word = _getw(fileref)) == EOF) break; + M[j] = word; + } + } + + printf("%d words %s [%06o - %06o].\n", j - lo, flag ? "dumped" : "loaded", lo, j-1); + + return SCPE_OK; +} + +/* +Original Operate-class instruction micro orders for the 1956 TX-0 Instruction Set + + Operate Fields + -------------- + --1 --- --- --- --- --- CLL 0.8 + --- 1-- --- --- --- --- CLR 0.8 + --- -10 --- --- --- --- IOS 0.8 + --- -11 --- --- --- --- HLT 1.8 + --- --- 111 --- --- --- P7H 0.8 + --- --- 110 --- --- --- P6H 0.8 + --- --- 100 --- --- --- PNT 0.8 + --- --- 001 --- --- --- R1C 0.8 + --- --- 011 --- --- --- R3C 0.8 + --- --- 010 --- --- --- DIS 0.8 + --- --- --- 10- --- --- SHR 1.4 + --- --- --- 11- --- --- CYR 1.4 + --- --- --- 01- --- --- MLR 1.3 + --- --- --- --1 --- 0-- PEN 1.1 + --- --- --- --0 --- 1-- TAC 1.1 + --- --- --- --- 1-- --- COM 1.2 + --- --- --- --- -1- --- PAD 1.4 + --- --- --- --- --1 --- CRY 1.7 + --- --- --- --- --- -01 AMB 1.2 AC -> MBR + --- --- --- --- --- -11 TBR 1.2 TBR -> MBR + --- --- --- --- --- -10 LMB 1.3 LR -> MBR +*/ +#define OOPR_CLL 0100000 +#define OOPR_CLR 0040000 +#define OOPR_IOS 0020000 +#define OOPR_HLT 0030000 +#define OOPR_IOS_MASK 0007000 +#define OOPR_P7H 0007000 +#define OOPR_P6H 0006000 +#define OOPR_PNT 0004000 +#define OOPR_R3C 0003000 +#define OOPR_DIS 0002000 +#define OOPR_R1C 0001000 + +#define OOPR_SHF_MASK 0000300 +#define OOPR_SHR 0000400 +#define OOPR_CYR 0000300 +#define OOPR_MLR 0000200 + +#define OOPR_PEN_MASK 0000104 +#define OOPR_PEN 0000100 + +#define OOPR_TAC_MASK 0000104 +#define OOPR_TAC 0000004 + +#define OOPR_COM 0000040 +#define OOPR_PAD 0000020 +#define OOPR_CRY 0000010 + +#define OOPR_AMB_MASK 0000007 +#define OOPR_AMB 0000001 +#define OOPR_TBR 0000003 +#define OOPR_LMB 0000002 + +t_stat sim_opr_orig(int32 op) +{ + t_stat reason = SCPE_OK; + + if (op & OOPR_CLL) { /* cll 0.8 Clear the left nine digital positions of the AC */ + AC &= 0000777; + TRACE_PRINT(ORD_MSG, ("[%06o]: CLL\n", PC-1)); + } + if (op & OOPR_CLR) { /* clr 0.8 Clear the right nine digital positions of the AC */ + AC &= 0777000; + TRACE_PRINT(ORD_MSG, ("[%06o]: CLR\n", PC-1)); + } + +/* IOS - In / Out Stop */ + /* Check TTI for character. If so, put in LR and set LR bit 0. */ + if (iosta & IOS_TTI) { + int32 rbuf; + rbuf = tti(0,0,0); + TRACE_PRINT(IOS_MSG, ("TTI: character received='%c'\n", rbuf &077)); + printf("TTI: character received='%c'\n", rbuf &077); + LR &= 0266666; /* Clear bits 0,2,5,8,...,17 */ + + LR |= SIGN; /* Set bit 0, character available. */ + LR |= ((rbuf & 001) >> 0) << 15;/* bit 2 */ + LR |= ((rbuf & 002) >> 1) << 12;/* bit 5 */ + LR |= ((rbuf & 004) >> 2) << 9; /* bit 8 */ + LR |= ((rbuf & 010) >> 3) << 6; /* bit 11 */ + LR |= ((rbuf & 020) >> 4) << 3; /* bit 14 */ + LR |= ((rbuf & 040) >> 5) << 0; /* bit 17 */ + } + + + + if ((op & OOPR_HLT) == OOPR_IOS) { /* I/O 0.8 IOS */ + TRACE_PRINT(IOS_MSG, ("[%06o] I/O Operation\n", PC-1)); + + switch (op & OOPR_IOS_MASK) { + case OOPR_P7H: + case OOPR_P6H: + { + uint32 tmpAC = 0; + tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */ + tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */ + tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */ + tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */ + tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */ + tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */ + tmpAC &= 0077; + if ((op & OOPR_IOS_MASK) == OOPR_P7H) { + tmpAC |= 0100; /* Punch 7th hole. */ + TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 7 holes\n", PC-1)); + } else { + TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 6 holes\n", PC-1)); + } + ptp (0, 0, tmpAC); /* Punch one character on TTO */ + } + break; + case OOPR_PNT: + { + uint32 tmpAC = 0; + tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */ + tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */ + tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */ + tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */ + tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */ + tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */ + tto (0, 0, tmpAC & 077); /* Print one character on TTO */ + } + break; + case OOPR_R3C: + AC = petr(3, 0, 0); + break; + case OOPR_R1C: + AC &= 0333333; /* Clear bits 0,3,6,9,12,15 */ + AC |= petr(1, 0, 0); + break; + case OOPR_DIS: + LP = dpy (AC); /* Display point on the CRT */ + break; + } + } + +/* 1.1 TAC and PEN */ + if ((op & OOPR_PEN_MASK) == OOPR_PEN) { /* pen 1.1 Read the light pen flip flops 1 and 2 into AC0 and AC1 */ + TRACE_PRINT(IOS_MSG, ("[%06o] Light Pen %01o\n", PC-1, LP)); + AC &= AMASK; + AC |= (LP & 1) << 17; + AC |= (LP & 2) << 16; + AC &= DMASK; + } + + if ((op & OOPR_TAC_MASK) == OOPR_TAC) { /* tac 1.1 Insert a one in each digital position of the AC whereever there is a one in the corresponding digital position of the TAC */ + TRACE_PRINT(IOS_MSG, ("[%06o] TAC %06o\n", PC-1, TAC)); + AC |= TAC; + } + + /* 1.2: COM, AMB, TBR */ + if (op & OOPR_COM) { /* com 1.2 Complement every digit in the accumulator */ + AC = ~AC; + inst_ctr.com++; + } + + switch (op & OOPR_AMB_MASK) { + case OOPR_AMB: + inst_ctr.amb++; + MBR = AC; + break; + case OOPR_TBR: + TRACE_PRINT(IOS_MSG, ("[%06o] TBR %06o\n", PC-1, TBR)); + MBR |= TBR; + break; + case OOPR_LMB: + MBR = LR; + inst_ctr.lmb++; + break; + } + + /* 1.3, 1.4: can these happen together? */ + switch (op & OOPR_SHF_MASK) { + case OOPR_MLR: + LR = MBR; + inst_ctr.mbl++; + break; + case OOPR_SHR: /* Shift AC Right, preserve bit 0. */ + { + int32 bit0; + bit0 = AC & 0400000; + AC = AC >> 1; + AC |= bit0; + inst_ctr.shr++; + break; + } + case OOPR_CYR: /* cyr 1.4 Cycle the AC right one digital position (AC17 -> AC0) */ + { + int32 bit17; + bit17 = (AC & 1) << 17; + AC >>= 1; + AC |= bit17; + inst_ctr.cyr++; + } + break; + } + + if (op & OOPR_PAD) { /* 1.5 Partial Add (XOR): AC = MBR ^ AC */ + if (op & OOPR_CRY) { /* 1.7 */ + TRACE_PRINT(ORD_MSG, ("[%06o] PAD+CRY: AC=%06o, MBR=%06o = ", PC-1, AC, MBR)); + AC = AC + MBR; + if (AC & 01000000) { + AC += 1; + } else; + AC &= DMASK; + TRACE_PRINT(ORD_MSG, ("%06o\n", AC)); + inst_ctr.cry++; + } else { + TRACE_PRINT(ORD_MSG, ("[%06o] PAD: AC=%06o, MBR=%06o\n", PC-1, AC, MBR)); + AC = AC ^ MBR; + AC &= DMASK; + TRACE_PRINT(ORD_MSG, ("[%06o] PAD: Check: AC=%06o\n", PC-1, AC)); + } + inst_ctr.pad++; + } + + if (op & OOPR_CRY) { /* 1.7 */ + if (op & OOPR_PAD) { + } else { + TRACE_PRINT(ERROR_MSG, ("[%06o] CRY: TODO: AC=%06o\n", PC-1, AC)); + inst_ctr.cry++; + } + } + + if ((op & OOPR_HLT) == OOPR_HLT) { /* hlt 1.8 Halt the computer */ + TRACE_PRINT(IOS_MSG, ("[%06o] HALT Instruction\n", PC-1)); + reason = STOP_HALT; + } + + return reason; +} diff --git a/TX-0/tx0_defs.h b/TX-0/tx0_defs.h new file mode 100644 index 00000000..58073561 --- /dev/null +++ b/TX-0/tx0_defs.h @@ -0,0 +1,93 @@ +/************************************************************************* + * * + * $Id: tx0_defs.h 2059 2009-02-23 05:59:14Z hharte $ * + * * + * Copyright (c) 2009 Howard M. Harte. * + * Based on pdp1_defs.h, Copyright (c) 1993-2006, 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 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 * + * of Howard M. Harte. * + * * + * Module Description: * + * cpu TX-0 Central Processor * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +#ifndef _PDP1_DEFS_H_ +#define _PDP1_DEFS_H_ 0 + +#include "sim_defs.h" + +/* Simulator stop codes */ +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ + +/* Memory */ +#define ASIZE 16 /* address bits */ +#define MAXMEMSIZE (1u << ASIZE) /* max mem size */ +#define AMASK (MAXMEMSIZE - 1) /* address mask */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* Architectural constants */ +#define SIGN 0400000 /* sign */ +#define DMASK 0777777 /* data mask */ +#define YMASK 0017777 /* "Y" Mask for address calculation (13 bits) */ + +/* I/O status flags */ +#define IOS_V_LPN 17 /* light pen */ +#define IOS_V_PETR 16 /* paper tape reader */ +#define IOS_V_TTO 15 /* typewriter out */ +#define IOS_V_TTI 14 /* typewriter in */ +#define IOS_V_PTP 13 /* paper tape punch */ +#define IOS_V_DRM 12 /* drum */ +#define IOS_V_SQB 11 /* sequence break */ +#define IOS_V_PNT 3 /* print done */ +#define IOS_V_SPC 2 /* space done */ +#define IOS_V_DCS 1 /* data comm sys */ +#define IOS_V_DRP 0 /* parallel drum busy */ + +#define IOS_LPN (1 << IOS_V_LPN) +#define IOS_PETR (1 << IOS_V_PETR) +#define IOS_TTO (1 << IOS_V_TTO) +#define IOS_TTI (1 << IOS_V_TTI) +#define IOS_PTP (1 << IOS_V_PTP) +#define IOS_DRM (1 << IOS_V_DRM) +#define IOS_SQB (1 << IOS_V_SQB) +#define IOS_PNT (1 << IOS_V_PNT) +#define IOS_SPC (1 << IOS_V_SPC) +#define IOS_DCS (1 << IOS_V_DCS) +#define IOS_DRP (1 << IOS_V_DRP) + +#define UNIT_V_MODE (UNIT_V_UF + 0) +#define UNIT_MODE (3 << UNIT_V_MODE) +#define UNIT_MODE_READIN (3 << UNIT_V_MODE) +#define UNIT_MODE_TEST (1 << UNIT_V_MODE) + + +#endif diff --git a/TX-0/tx0_diag.txt b/TX-0/tx0_diag.txt new file mode 100644 index 00000000..e7fdabd0 --- /dev/null +++ b/TX-0/tx0_diag.txt @@ -0,0 +1,16 @@ + +Programs that are known to work well: + +bin_tic-tac-toe_new_code_12-16-61.bin, seems to fully work: + sim> boot petr + sim> g (will halt with 205200 in LR) + sim> e lr + LR: 205200 + sim> g (again to start game.) +bin_tstDisplay.bin, makes a diagonal line on the DPY +bin_lightGunTst.bin, can draw on screen with light pen. + + +bin_newMouse_3-22-66.bin - These kind of work, but mouse gives up easy +bin_newMouse_8-14-61.bin - and can crash on complex mazes. Sim bug + somewhere. diff --git a/TX-0/tx0_dpy.c b/TX-0/tx0_dpy.c new file mode 100644 index 00000000..09d193b2 --- /dev/null +++ b/TX-0/tx0_dpy.c @@ -0,0 +1,139 @@ +/************************************************************************* + * * + * $Id: tx0_dpy.c 2060 2009-02-24 06:49:07Z hharte $ * + * * + * Copyright (c) 2009-2012, Howard M. Harte * + * Copyright (c) 2004, Philip L. Budne * + * Copyright (c) 1993-2003, 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 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 * + * of Howard M. Harte. * + * * + * Module Description: * + * TX-0 display simulator * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +#ifdef USE_DISPLAY +#include "tx0_defs.h" +#include "display/display.h" + +extern int32 ios, iosta, PF; +extern int32 stop_inst; +extern int32 PEN_HIT; + +t_stat dpy_svc (UNIT *uptr); +t_stat dpy_reset (DEVICE *dptr); + +/* DPY data structures + dpy_dev DPY device descriptor + dpy_unit DPY unit + dpy_reg DPY register list +*/ + +#define CYCLE_TIME 5 /* 5us memory cycle */ +#define DPY_WAIT (50/CYCLE_TIME) /* 50us */ + +UNIT dpy_unit = { + UDATA (&dpy_svc, UNIT_ATTABLE, 0), DPY_WAIT }; + +DEVICE dpy_dev = { + "DPY", &dpy_unit, NULL, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &dpy_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE }; + +/* Display Routine */ +int32 dpy (int32 ac) +{ + int32 pen_hit; + int32 x, y; + int level; + + if (dpy_dev.flags & DEV_DIS) /* disabled? */ + return SCPE_UDIS; + + x = (ac >> 9) & 0777; /* X = high nine bits of AC */ + y = (ac & 0777); /* Y = low nine bits of AC */ + + /* + * convert one's complement -255..+255 center origin + * to 0..511 (lower left origin) + */ + if (x & 0400) + x ^= 0400; + else + x += 255; + if (y & 0400) + y ^= 0400; + else + y += 255; + + level = DISPLAY_INT_MAX; /* Maximum intensity */ + + if (display_point(x,y,level,0)) { + /* here with light pen hit */ + PF = PF | 010; /* set prog flag 3 */ + pen_hit = 1; + + } else { + pen_hit = 0; + } + + sim_activate (&dpy_unit, dpy_unit.wait); /* activate */ + + return pen_hit; +} + +/* + * Unit service routine + * + * Under X11 this includes polling for events, so it can't be + * call TOO infrequently... + */ +t_stat dpy_svc (UNIT *uptr) +{ + display_age(dpy_unit.wait*CYCLE_TIME, 1); + sim_activate (&dpy_unit, dpy_unit.wait); /* requeue! */ + return SCPE_OK; +} + +/* Reset routine */ + +t_stat dpy_reset (DEVICE *dptr) +{ + display_init(DIS_TX0, RES_FULL); + display_reset(); + iosta = iosta & ~(IOS_PNT | IOS_SPC); /* clear flags */ + sim_cancel (&dpy_unit); /* deactivate unit */ + return SCPE_OK; +} + +#else /* USE_DISPLAY not defined */ +char tx0_dpy_unused; /* sometimes empty object modules cause problems */ +#endif /* USE_DISPLAY not defined */ diff --git a/TX-0/tx0_stddev.c b/TX-0/tx0_stddev.c new file mode 100644 index 00000000..7351b480 --- /dev/null +++ b/TX-0/tx0_stddev.c @@ -0,0 +1,623 @@ +/************************************************************************* + * * + * $Id: tx0_stddev.c 2063 2009-02-25 07:37:57Z hharte $ * + * * + * Copyright (c) 2009-2012 Howard M. Harte. * + * Based on pdp1_stddev.c, Copyright (c) 1993-2006, 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 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 * + * of Howard M. Harte. * + * * + * Module Description: * + * TX-0 Standard Devices * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/* + petr paper tape reader + ptp paper tape punch + tti keyboard + tto teleprinter + + Note: PTP timeout must be >10X faster than TTY output timeout for Macro + to work correctly! +*/ + +#include "tx0_defs.h" + +#define FLEXO_STOP 061 /* stop code */ +#define FLEXO_UC 071 +#define FLEXO_LC 075 +#define UC_V 6 /* upper case */ +#define UC (1 << UC_V) +#define BOTH (1 << (UC_V + 1)) /* both cases */ +#define CW (1 << (UC_V + 2)) /* char waiting */ +#define TT_WIDTH 077 +#define UNIT_V_ASCII (UNIT_V_UF + 0) /* ASCII/binary mode */ +#define UNIT_ASCII (1 << UNIT_V_ASCII) +#define PETR_LEADER 20 /* ASCII leader chars */ + +#define TRACE_PRINT(dev, level, args) if(dev.dctrl & level) { \ + printf args; \ + } + +int32 petr_state = 0; +int32 petr_wait = 0; +int32 petr_stopioe = 0; +int32 petr_uc = 0; /* upper/lower case */ +int32 petr_hold = 0; /* holding buffer */ +int32 petr_leader = PETR_LEADER; /* leader count */ +int32 ptp_stopioe = 0; +int32 tti_hold = 0; /* tti hold buf */ +int32 tty_buf = 0; /* tty buffer */ +int32 tty_uc = 0; /* tty uc/lc */ +int32 tto_sbs = 0; + +extern int32 ios, iosta; +extern int32 PF, IR, PC, TA; +extern int32 M[]; + +t_stat petr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat petr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat tty_reset (DEVICE *dptr); +t_stat petr_boot (int32 unitno, DEVICE *dptr); +t_stat petr_attach (UNIT *uptr, char *cptr); + +/* Character translation tables */ + +int32 flexo_to_ascii[128] = { +/*00*/ 0, 0, 'e', '8', 0, '|', 'a', '3', /* lower case */ +/*10*/ ' ', '=', 's', '4', 'i', '+', 'u', '2', +/*20*/ 0, '.', 'd', '5', 'r', '1', 'j', '7', +/*30*/ 'n', ',', 'f', '6', 'c', '-', 'k', 0, +/*40*/ 't', 0, 'z', '\b','l', '\t','w', 0, +/*50*/ 'h', '\r','y', 0, 'p', 0, 'q', 0, +/*60*/ 'o', '*', 'b', 0, 'g', 0, '9', 0, +/*70*/ 'm', 0, 'x', 0, 'v', 0, '0', 0, +/*00*/ 0, 0, 'E', '8', 0, '_', 'A', '3', /* upper case */ +/*10*/ ' ', ':', 'S', '4', 'I', '/', 'U', '2', +/*20*/ 0, ')', 'D', '5', 'R', '1', 'J', '7', +/*30*/ 'N', '(', 'F', '6', 'C', '-', 'K', 0, +/*40*/ 'T', 0, 'Z', '\b','L', '\t','W', 0, +/*50*/ 'H', '\r','Y', 0, 'P', 0, 'Q', 0, +/*60*/ 'O', '*', 'B', 0, 'G', 0, '9', 0, +/*70*/ 'M', 0, 'X', 0, 'V', 0, '0', 0, + }; + +int32 ascii_to_flexo[128] = { +/*00*/ 0, 0, 0, BOTH+061, 0, 0, 0, 0, /* STOP mapped to ^C */ +/*10*/ BOTH+043, BOTH+045, 0, 0, 0, BOTH+051, 0, 0, +/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, +/*30*/ 0, 0, 0, BOTH+020, 0, 0, 0, 0, /* Color Shift mapped to ESC */ +/*40*/ BOTH+010, 0, 0, 0, 0, 0, 0, 0, /* " ", */ +/*50*/ UC+021, UC+031, 021, 015, 031, UC+035, UC+011, UC+015, /* ()*+,-./ */ +/*60*/ 076, 025, 017, 007, 013, 023, 033, 027, /* 0-7 */ +/*70*/ 003, 066, 0, 0, 0, 011, 0, 0, /* 89:;<=>? */ +/*00*/ 040, UC+006, UC+062, UC+034, UC+022, UC+002, UC+032, UC+064, /* A-G */ +/*10*/ UC+050, UC+014, UC+026, UC+036, UC+044, UC+070, UC+030, UC+060, /* H-O */ +/*20*/ UC+054, UC+056, UC+024, UC+012, 040, 016, 074, 046, /* P-W */ +/*30*/ UC+072, UC+052, UC+042, 0, 0, 0, 0, UC+005, /* X-Z, */ +/*40*/ 00, 006, 062, 034, 022, 002, 032, 064, /* a-g */ +/*50*/ 050, 014, 026, 036, 044, 070, 030, 060, /* h-o */ +/*60*/ 054, 056, 024, 012, 040, 016, 074, 046, /* p-w */ +/*70*/ 072, 052, 042, 0, 005, 0, UC+035, BOTH+077 /* x-z, */ + }; + +/* PETR data structures + + petr_dev PETR device descriptor + petr_unit PETR unit + petr_reg PETR register list +*/ + +UNIT petr_unit = { + UDATA (&petr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), + SERIAL_IN_WAIT + }; + +REG petr_reg[] = { + { ORDATA (BUF, petr_unit.buf, 18) }, + { FLDATA (UC, petr_uc, UC_V) }, + { FLDATA (DONE, iosta, IOS_V_PETR) }, + { ORDATA (HOLD, petr_hold, 9), REG_HRO }, + { ORDATA (STATE, petr_state, 5), REG_HRO }, + { FLDATA (WAIT, petr_wait, 0), REG_HRO }, + { DRDATA (POS, petr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, petr_unit.wait, 24), PV_LEFT }, + { DRDATA (LEADER, petr_leader, 6), REG_HRO }, + { FLDATA (STOP_IOE, petr_stopioe, 0) }, + { NULL } + }; + +MTAB petr_mod[] = { + { UNIT_ASCII, UNIT_ASCII, "ASCII", "ASCII", NULL }, + { UNIT_ASCII, 0, "FLEXO", "FLEXO", NULL }, + { 0 } + }; + +/* Debug flags */ +#define ERROR_MSG (1 << 0) +#define TRACE_MSG (1 << 1) +#define VERBOSE_MSG (1 << 2) + +/* Debug Flags */ +static DEBTAB petr_dt[] = { + { "ERROR", ERROR_MSG }, + { "TRACE", TRACE_MSG }, + { "VERBOSE",VERBOSE_MSG }, + { NULL, 0 } +}; + +DEVICE petr_dev = { + "PETR", &petr_unit, petr_reg, petr_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &petr_reset, + &petr_boot, &petr_attach, NULL, + NULL, DEV_DEBUG, (ERROR_MSG), + petr_dt, NULL + }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit + ptp_reg PTP register list +*/ + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT + }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (DONE, iosta, IOS_V_PTP) }, + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } + }; + +MTAB ptp_mod[] = { + { 0 } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, ptp_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL, + NULL, DEV_DEBUG, (ERROR_MSG|TRACE_MSG), + petr_dt, NULL + }; + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit + tti_reg TTI register list +*/ + +UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tty_buf, 6) }, + { FLDATA (UC, tty_uc, UC_V) }, + { ORDATA (HOLD, tti_hold, 9), REG_HRO }, + { FLDATA (DONE, iosta, IOS_V_TTI) }, + { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB tti_mod[] = { + { 0 } + }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tty_reset, + NULL, NULL, NULL, + NULL, DEV_DEBUG, (ERROR_MSG|TRACE_MSG), + petr_dt, NULL + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit + tto_reg TTO register list +*/ + +UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT * 10 }; + +REG tto_reg[] = { + { ORDATA (BUF, tty_buf, 6) }, + { FLDATA (UC, tty_uc, UC_V) }, + { FLDATA (DONE, iosta, IOS_V_TTO) }, + { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tto_mod[] = { + { 0 } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tty_reset, + NULL, NULL, NULL, + NULL, DEV_DEBUG, (ERROR_MSG|TRACE_MSG), + petr_dt, NULL + }; + +/* Paper tape reader: IOT routine. Points to note: + + - RPA (but not RPB) complements the reader clutch control. Thus, + if the reader is running, RPA will stop it. + - The status bit indicates data in the reader buffer that has not + been transfered to IR. It is cleared by any RB->IR operation, + including RRB and the completion pulse. + - A reader error on a wait mode operation could hang the simulator. + IOH is set; any retry (without RESET) will be NOP'd. Accordingly, + the PETR service routine clears IOH on any error during a rpa/rpb i. +*/ + +int32 petr (int32 inst, int32 dev, int32 dat) +{ + int32 tmpAC = 0; + int i = 0; + t_stat result; + ios = 1; + + for (i=0;i> 0) << 17; /* bit 0 */ + tmpAC |= ((petr_unit.buf & 002) >> 1) << 14; /* bit 3 */ + tmpAC |= ((petr_unit.buf & 004) >> 2) << 11; /* bit 6 */ + tmpAC |= ((petr_unit.buf & 010) >> 3) << 8; /* bit 9 */ + tmpAC |= ((petr_unit.buf & 020) >> 4) << 5; /* bit 12 */ + tmpAC |= ((petr_unit.buf & 040) >> 5) << 2; /* bit 15 */ + + if (i < (inst-1)) { + uint32 bit0 = (tmpAC & 1) << 17; + TRACE_PRINT(petr_dev, TRACE_MSG, ("PETR read [%04x=0x%02x] %03o\n", petr_unit.pos-1, petr_unit.buf, petr_unit.buf)); + tmpAC >>= 1; + tmpAC |= bit0; + } else { + TRACE_PRINT(petr_dev, TRACE_MSG, ("PETR read [%04x=0x%02x] %03o, tmpAC=%06o\n", petr_unit.pos-1, petr_unit.buf, petr_unit.buf, tmpAC)); + } + } + return tmpAC; + + /* sim_activate (&petr_unit, petr_unit.wait); */ /* start reader */ + +} + +/* Unit service */ + +t_stat petr_svc (UNIT *uptr) +{ +int32 temp; + +if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ + ios = 0; + return SCPE_OK; + } + + if ((temp = getc (uptr->fileref)) != EOF) { /* no, get raw char */ + uptr->pos = uptr->pos + 1; /* if not eof, count */ + } + if (temp == EOF) { /* end of file? */ + if (feof (uptr->fileref)) { + ios = 0; + return SCPE_IOERR; + } + else perror ("PETR I/O error"); + clearerr (uptr->fileref); + ios = 0; + return SCPE_IOERR; + } + + uptr->buf = temp; + +ios = 0; + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat petr_reset (DEVICE *dptr) +{ + petr_state = 0; /* clear state */ + petr_wait = 0; + petr_hold = 0; + petr_uc = 0; + petr_unit.buf = 0; + iosta = iosta & ~IOS_PETR; /* clear flag */ + sim_cancel (&petr_unit); /* deactivate unit */ + return SCPE_OK; +} + +/* Attach routine */ + +t_stat petr_attach (UNIT *uptr, char *cptr) +{ + petr_leader = PETR_LEADER; /* set up leader */ + return attach_unit (uptr, cptr); +} + +/* Bootstrap routine */ +extern t_stat cpu_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +extern UNIT cpu_unit; + +//#define SANITY_CHECK_TAPE + +/* Switches the CPU to READIN mode and starts execution. */ +t_stat petr_boot (int32 unitno, DEVICE *dptr) +{ + t_stat reason = SCPE_OK; + +#ifdef SANITY_CHECK_TAPE + int32 AC, MBR, MAR, IR = 0; + int32 blkcnt, chksum = 0, fa, la; + int32 addr, tdata; +#endif /* SANITY_CHECK_TAPE */ + + /* Switch to READIN mode. */ + cpu_set_mode(&cpu_unit, UNIT_MODE_READIN, NULL, NULL); +#ifdef SANITY_CHECK_TAPE + for(;(IR != 2) && (IR != 1);) { + AC = petr(3,0,0); /* Read three chars from tape into AC */ + MAR = AC & AMASK; /* Set memory address */ + IR = AC >> 16; + + if (!MEM_ADDR_OK(MAR)) { + TRACE_PRINT(petr_dev, ERROR_MSG, ("READIN: Tape address out of range.\n")); + reason = SCPE_FMT; + } + + switch (IR) { + case 00: /* Storage (sto x) */ + case 03: /* Storage (opr x) */ + MBR = petr(3,0,0); /* Read three characters from tape. */ + TRACE_PRINT(petr_dev, ERROR_MSG, ("READIN: sto @%06o = %06o\n", MAR, MBR)); + printf("[%06o] = %06o\n", MAR, MBR); + break; + case 02: /* Transfer Control (trn x) Start Execution */ + PC = MAR; + reason = SCPE_OK; /* let SIMH start execution. */ + TRACE_PRINT(petr_dev, ERROR_MSG, ("READIN: trn %06o (Start Execution)\n", PC)); + reason = cpu_set_mode(&cpu_unit, 0, NULL, NULL); + break; + case 01: /* Transfer (add x) - Halt */ + PC = MAR; + reason = SCPE_STOP; /* let SIMH halt. */ + TRACE_PRINT(petr_dev, ERROR_MSG, ("READIN: add %06o (Halt)\n", PC)); + reason = cpu_set_mode(&cpu_unit, 0, NULL, NULL); + break; + default: + reason = SCPE_IERR; + break; + } + } + + blkcnt = 0; + while (1) { + chksum = 0; + + fa = petr(3,0,0); /* Read three characters from tape. */ + + if ((fa & 0400000) || (fa & 0200000)) { + break; + } + + chksum += fa; + if (chksum > 0777777) { + chksum +=1; + } + chksum &= 0777777; + + la = petr(3,0,0); /* Read three characters from tape. */ + + chksum += la; + if (chksum > 0777777) { + chksum +=1; + } + chksum &= 0777777; + + la = (~la) & 0177777; + + printf("First Address=%06o, Last Address=%06o\n", fa, la); + + for(addr = fa; addr <= la; addr++) { + tdata = petr(3,0,0); /* Read three characters from tape. */ + chksum += tdata; + if (chksum > 0777777) { + chksum +=1; + } + chksum &= 0777777; + } + + chksum = (~chksum) & 0777777; + + tdata = petr(3,0,0); + + if (chksum != tdata) { + reason = SCPE_FMT; + } + + printf("Block %d: Calculated checksum=%06o, real checksum=%06o, %s\n", blkcnt, chksum, tdata, chksum == tdata ? "OK" : "BAD Checksum!"); + blkcnt++; + } + + fseek (petr_dev.units[0].fileref, 0, SEEK_SET); +#endif /* SANITY_CHECK_TAPE */ + + /* Start Execution */ + return (reason); + +} + +/* Paper tape punch: IOT routine */ + +int32 ptp (int32 inst, int32 dev, int32 dat) +{ + iosta = iosta & ~IOS_PTP; /* clear flag */ + ptp_unit.buf = dat & 0177; + ptp_svc (&ptp_unit); + /* sim_activate (&ptp_unit, ptp_unit.wait); */ /* start unit */ + return dat; +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ + ios = 1; /* restart */ + iosta = iosta | IOS_PTP; /* set flag */ + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return SCPE_UNATT; + if (putc (uptr->buf, uptr->fileref) == EOF) { /* I/O error? */ + perror ("PTP I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + uptr->pos = uptr->pos + 1; + return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ + ptp_unit.buf = 0; /* clear state */ + iosta = iosta & ~IOS_PTP; /* clear flag */ + sim_cancel (&ptp_unit); /* deactivate unit */ + return SCPE_OK; +} + +/* Typewriter IOT routines */ + +int32 tti (int32 inst, int32 dev, int32 dat) +{ + iosta = iosta & ~IOS_TTI; /* clear flag */ + return tty_buf & 077; +} + +int32 tto (int32 inst, int32 dev, int32 dat) +{ + tty_buf = dat & TT_WIDTH; /* load buffer */ + ios = 0; + tto_svc(&tto_unit); + /* sim_activate (&tto_unit, tto_unit.wait); */ /* activate unit */ + return dat; +} + +/* Unit service routines */ + +t_stat tti_svc (UNIT *uptr) +{ + int32 in = 0, temp = 0; + + sim_activate (uptr, uptr->wait); /* continue poll */ + if (tti_hold & CW) { /* char waiting? */ + tty_buf = tti_hold & TT_WIDTH; /* return char */ + tti_hold = 0; /* not waiting */ + } else { + if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; + if (temp & SCPE_BREAK) return SCPE_OK; /* ignore break */ + temp = temp & 0177; + if (temp == 0177) temp = '\b'; /* rubout? bs */ + sim_putchar (temp); /* echo */ + if (temp == '\r') sim_putchar ('\n'); /* cr? add nl */ + in = ascii_to_flexo[temp]; /* translate char */ + + if (in == 0) return SCPE_OK; /* no xlation? */ + if ((in & BOTH) || ((in & UC) == (tty_uc & UC))) { + tty_buf = in & TT_WIDTH; + } else { /* must shift */ + tty_uc = in & UC; /* new case */ + tty_buf = tty_uc? FLEXO_UC: FLEXO_LC; + tti_hold = in | CW; /* set 2nd waiting */ + } + } + iosta = iosta | IOS_TTI; /* set flag */ + TRACE_PRINT(tti_dev, TRACE_MSG, ("TTI read ASCII: %02x / FLEXO=%03o\n", temp, tty_buf)); + uptr->pos = uptr->pos + 1; + return SCPE_OK; +} + +t_stat tto_svc (UNIT *uptr) +{ + int32 c = 0; + t_stat r; + + if (tty_buf == FLEXO_UC) tty_uc = UC; /* upper case? */ + else if (tty_buf == FLEXO_LC) tty_uc = 0; /* lower case? */ + else { + c = flexo_to_ascii[tty_buf | tty_uc]; /* translate */ + if (c && ((r = sim_putchar_s (c)) != SCPE_OK)) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* retry */ + return ((r == SCPE_STALL)? SCPE_OK: r); + } + } + iosta = iosta | IOS_TTO; /* set flag */ + uptr->pos = uptr->pos + 1; + if (c == '\r') { /* cr? add lf */ + sim_putchar ('\n'); + uptr->pos = uptr->pos + 1; + } + return SCPE_OK; +} + +/* Reset routine */ + +t_stat tty_reset (DEVICE *dptr) +{ + tty_buf = 0; /* clear buffer */ + tty_uc = 0; /* clear case */ + tti_hold = 0; /* clear hold buf */ + iosta = (iosta & ~IOS_TTI) | IOS_TTO; /* clear flag */ + sim_activate (&tti_unit, tti_unit.wait); /* activate keyboard */ + sim_cancel (&tto_unit); /* stop printer */ + return SCPE_OK; +} diff --git a/TX-0/tx0_sys.c b/TX-0/tx0_sys.c new file mode 100644 index 00000000..a571ecac --- /dev/null +++ b/TX-0/tx0_sys.c @@ -0,0 +1,439 @@ +/************************************************************************* + * * + * $Id: tx0_sys.c 2061 2009-02-24 07:05:58Z hharte $ * + * * + * Copyright (c) 2009-2012 Howard M. Harte. * + * Based on pdp1_sys.c, Copyright (c) 1993-2007, 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 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 * + * of Howard M. Harte. * + * * + * Module Description: * + * TX-0 simulator interface * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +#include "tx0_defs.h" +#include + +extern DEVICE cpu_dev; +extern DEVICE petr_dev; +extern DEVICE tto_dev; +extern DEVICE tti_dev; +extern DEVICE ptp_dev; +#ifdef USE_DISPLAY +extern DEVICE dpy_dev; +#endif /* USE_DISPLAY */ + +#ifdef USE_FPC +extern DEVICE fpc_dev; +#endif /* USE_FPC */ +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern int32 M[]; +extern int32 PC; +extern int32 ascii_to_flexo[], flexo_to_ascii[]; +extern int32 sc_map[]; +extern int32 sim_switches; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "TX-0"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &petr_dev, + &tti_dev, + &tto_dev, + &ptp_dev, +#ifdef USE_DISPLAY + &dpy_dev, +#endif/* USE_DISPLAY */ +#ifdef USE_FPC + &fpc_dev, +#endif /* USE_FPC */ + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Undefined instruction", + "HALT instruction", + "Breakpoint", + "Nested XCT's", + "Nested indirect addresses", + "Infinite I/O wait state", + "DECtape off reel" + }; + +int32 tx0_getw (FILE *inf) +{ +int32 i, tmp, word; + +word = 0; +for (i = 0; i < 3;) { + if ((tmp = getc (inf)) == EOF) return -1; + if (tmp & 0200) { + word = (word << 6) | (tmp & 077); + i++; + } + } +return word; +} + +/* Symbol tables */ +typedef struct { + int32 opr; + char *mnemonic; + char *desc; +} OPMAP; + +typedef struct { + char *mnemonic; + char *desc; +} INSTMAP; + +const INSTMAP instmap[] = { +/* Store Class */ + { "sto", "Store AC" }, + { "stx", "Store AC, Indexed" }, + { "sxa", "Store XR in Address" }, + { "ado", "Add One" }, + { "slr", "Store LR" }, + { "slx", "Store LR, Indexed" }, + { "stz", "Store Zero" }, + { "[!sto-nop]", "NOP" }, + +/* Add Class */ + { "add", "Add" }, + { "adx", "Add, Indexed" }, + { "ldx", "Load XR" }, + { "aux", "Augment XR" }, + { "llr", "Load LR" }, + { "llx", "Load LR, Indexed" }, + { "lda", "Load AC" }, + { "lax", "Load AC, Indexed" }, + +/* Transfer Class */ + { "trn", "Transfer Negative" }, + { "trz", "Transfer +/- Zero" }, + { "tsx", "Transfer and set Index" }, + { "tix", "Transfer and Index" }, + { "tra", "Transfer" }, + { "trx", "Transfer Indexed" }, + { "tlv", "Transfer on external Level" }, + { "[!tra-nop]", "NOP" } +}; + +const OPMAP opmap [] = { + { 0600000, "opr", "No operation" }, + { 0600001, "xro", "Clear XR to +0" }, + { 0600003, "lxr", "Place LR in XR" }, + { 0600012, "cry", "Carry the contents of AC according to bits of LR" }, + { 0600022, "lpd", "Logical exclusive or of AC is placed in AC (partial add)" }, + { 0600032, "lad", "Add LR to AC" }, + { 0600040, "com", "Compliment the AC" }, + { 0600072, "lcd", "Contents of LR minus those of AC are placed in AC" }, + { 0600130, "xad", "Add index register to accumulator" }, + { 0600170, "xcd", "Contents of XR minus those of AC are placed in AC" }, + { 0600200, "lro", "Clear LR to +0" }, + { 0600300, "xlr", "Place XR in LR" }, + { 0600303, "ixl", "Interchange XR and LR" }, + { 0600400, "shr", "Shift accumulator right one place, bit 0 remains unchanged" }, + { 0600600, "cyr", "Cycle AC right one place" }, + { 0603000, "pen", "Contents of light pen and light cannon flip-flops replace contents of AC bits 0 and 1. The flip-flops are cleared." }, + { 0604000, "bsr", "Backspace tape unit by one record" }, + { 0604004, "rtb", "Read tape binary (odd parity)" }, + { 0604004, "rds", "Select tape unit for reading a record" }, + { 0604010, "rew", "Rewind tape unit" }, + { 0604014, "wtb", "Write tape binary (odd parity)" }, + { 0604014, "wrs", "Select tape unit for writing a record" }, + { 0604024, "rtd", "Read tape decimal (even parity)" }, + { 0604034, "wtd", "Write tape decimal (even parity)" }, + { 0607000, "cpf", "The program flag is cleared" }, + { 0620000, "cpy", "Transmit information between the live register and selected input-output unit" }, + { 0622000, "dis", "Display point on CRT corresponding to contents of AC" }, + { 0624000, "prt", "Print one on-line flexo character from bits 2, 5, etc." }, + { 0624600, "pnt", "PRT, then cycle AC right once to set up another character" }, + { 0625000, "typ", "Read one character from on-line flexowriter into LR bits 12-17" }, + { 0626600, "p6h", "Punch one line of paper tape; 6 holes from bits 2, 5, etc. of AC then cycle right once." }, + { 0627600, "p7h", "Same as p6h, but punch 7th hole" }, + { 0630000, "hlt", "Stops computer" }, + { 0631000, "cll", "Clear left half of AC to zero" }, + { 0632000, "clr", "Clear right half of AC" }, + { 0632022, "---", "CLR+PAD+LMB" }, + { 0640001, "axr", "Place AC contents in XR" }, + { 0640021, "axo", "AXR, then set AC to +0" }, + { 0640030, "cyl", "Cycle AC left one place" }, + { 0640031, "alx", "AXR, then cycle AC left once" }, + { 0640040, "amz", "Add minus zero to AC" }, + { 0640061, "axc", "AXR, then set AC to -0" }, + { 0640200, "alr", "Place accumulator contents in live register" }, + { 0640201, "---", "ALR+MBX, Place accumulator contents in live register, Transfer MBR to XR." }, + { 0640203, "rax", "Place LR in XR, then place AC in LR" }, + { 0640205, "orl", "Logical or of AC and LR is placed in LR" }, + { 0640207, "anl", "Logical and of AC and LR is placed in LR" }, + { 0640220, "alo", "ALR, then set AC to +0" }, + { 0640230, "all", "ALR, then cycle left once" }, + { 0640231, "---", "AMB+MBL+PAD+CRY+MBX" }, + { 0640232, "iad", "Interchange and add AC contents are placed in the LR and the previous contents of the LR ar added to AC" }, + { 0640260, "alc", "ALR, then set AC to -0" }, + { 0640601, "arx", "AXR, then cycle AC right once" }, + { 0647000, "spf", "Place AC in program flag register" }, + { 0662020, "dso", "DIS, then clear AC" }, + { 0664020, "pno", "PRT, then clear AC" }, + { 0664060, "pnc", "PRT, then clear AC to -0" }, + { 0666020, "p6o", "p6h then clear AC" }, + { 0667020, "p7o", "p7h then clear AC" }, + { 0700000, "cla", "Clear entire AC to +0" }, + { 0700001, "cax", "Clear AC and XR to +0" }, + { 0700012, "lal", "Place LR in AC cycled left once" }, + { 0700022, "lac", "Place LR in AC" }, + { 0700040, "clc", "Clear and complement: set AC to -0" }, + { 0700062, "lcc", "Place complement of LR in AC" }, + { 0700072, "laz", "Add LR to minus zero in AC" }, + { 0700110, "xal", "XAC, then cycle AC left once" }, + { 0700120, "xac", "Place index register in accumulator" }, + { 0700160, "xcc", "Place complement of XR in accumulator" }, + { 0700200, "cal", "Clear AC and LR to +0" }, + { 0700322, "rxe", "Place LR in AC, then place XR in LR" }, + { 0700622, "lar", "Place LR in AC cycled right once" }, + { 0701000, "tac", "Contents of test accumulator are placed in AC" }, + { 0702020, "tbr", "Contents of test buffer register are placed in AC" }, + { 0703000, "---", "Clear AC and read light pen" }, + { 0706020, "rpf", "The program flag register is placed in AC" }, + { 0721000, "rlc", "Read one line paper tape into AC bits 0, 3, etc." }, + { 0721600, "rlr", "rlc, then cycle AC right once" }, + { 0723000, "r3c", "Read three lines of paper tape" }, + { 0723032, "---", "R3C+LMB+PAD+CRY" }, + { 0726000, "p6a", "Clear AC and punch a line of blank tape" }, + { 0740025, "ora", "Logical or of AC and LR is placed in AC" }, + { 0740027, "ana", "Logical and of AC and LR is placed in AC" }, + { 0740207, "anc", "ANL, then clear AC" }, + { 0740205, "oro", "ORL, then clear AC" }, + { 0740222, "ial", "Interchange AC and LR" }, + { 0763232, "---", "AMB+CLA+R3L+MBL+LMB+PAD+CRY" }, + { 0766020, "p6b", "Punch a line of blank tape, but save AC" }, + { 0000000, NULL, NULL } +}; + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) +#define SIXTOASC(x) flexo_to_ascii[x] +#define ASCTOSIX(x) (ascii_to_flexo[x] & 077) + +extern int32 cpu_get_mode (void); +extern t_stat fprint_sym_orig (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, inst, op; + +if(!cpu_get_mode()) { + return fprint_sym_orig (of, addr, val, uptr, sw); +} + + +inst = val[0]; +cflag = (uptr == NULL) || (uptr == &cpu_unit); +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (sw & SWMASK ('F')) { + fputc (flexo_to_ascii[inst & 077], of); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* character? */ + fprintf (of, "%c", SIXTOASC ((inst >> 12) & 077)); + fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077)); + fprintf (of, "%c", SIXTOASC (inst & 077)); + return SCPE_OK; + } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + + op = (inst >> 13) & 037; + + if ((op & 030) != 030) /* sto, add, trn (not an opr) */ + { + fprintf (of, "%s %05o (%s)", instmap[op].mnemonic, inst & 017777, instmap[op].desc); + } else { /* opr */ + for(i=0;opmap[i].opr != 0;i++) { + if(inst == opmap[i].opr) { + fprintf (of, "opr %s (%s)", opmap[i].mnemonic, opmap[i].desc); + } + } + } +return SCPE_OK; +} + +/* Get 18b signed number + + Inputs: + *cptr = pointer to input string + *sign = pointer to sign + *status = pointer to error status + Outputs: + val = output value +*/ + +t_value get_sint (char *cptr, int32 *sign, t_stat *status) +{ +*sign = 1; +if (*cptr == '+') { + *sign = 0; + cptr++; + } +else if (*cptr == '-') { + *sign = -1; + cptr++; + } +return get_uint (cptr, 8, DMASK, status); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +#if 0 + int32 cflag, d, i, j, k, sign; +t_stat r; +static int32 sc_enc[10] = { 0, 01, 03, 07, 017, 037, 077, 0177, 0377, 0777 }; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; +for (i = 1; (i < 3) && (cptr[i] != 0); i++) + if (cptr[i] == 0) for (j = i + 1; j <= 3; j++) cptr[j] = 0; +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0]; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((ASCTOSIX (cptr[0]) & 077) << 12) | + ((ASCTOSIX (cptr[1]) & 077) << 6) | + (ASCTOSIX (cptr[2]) & 077); + return SCPE_OK; + } + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & DMASK; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_LAW: /* LAW */ + cflag = 0; /* fall through */ + case I_V_MRF: case I_V_MRI: /* mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((j != I_V_MRI) && strcmp (gbuf, "I") == 0) { /* indirect? */ + val[0] = val[0] | IA; + cptr = get_glyph (cptr, gbuf, 0); + } + d = get_uint (gbuf, 8, AMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (d <= DAMASK) val[0] = val[0] | d; + else if (cflag && (((addr ^ d) & EPCMASK) == 0)) + val[0] = val[0] | (d & DAMASK); + else return SCPE_ARG; + break; + + case I_V_SHF: /* shift */ + cptr = get_glyph (cptr, gbuf, 0); + d = get_uint (gbuf, 10, 9, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | sc_enc[d]; + break; + + case I_V_NPN: case I_V_IOT: + case I_V_OPR: case I_V_SKP: case I_V_SPC: + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0); i++) ; + if (opcode[i] != NULL) { + k = opc_val[i] & DMASK; + if ((k != IA) && (((k ^ val[0]) & 0760000) != 0)) + return SCPE_ARG; + val[0] = val[0] | k; + } + else { + d = get_sint (gbuf, &sign, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (sign == 0) val[0] = val[0] + d; + else if (sign < 0) val[0] = val[0] - d; + else val[0] = val[0] | d; + } + } + break; + } /* end case */ +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +#endif +return SCPE_ARG; +} diff --git a/TX-0/tx0_sys_orig.c b/TX-0/tx0_sys_orig.c new file mode 100644 index 00000000..a85c9d32 --- /dev/null +++ b/TX-0/tx0_sys_orig.c @@ -0,0 +1,133 @@ +/************************************************************************* + * * + * $Id: tx0_sys_orig.c 2065 2009-02-25 15:05:00Z hharte $ * + * * + * Copyright (c) 2009-2012 Howard M. Harte. * + * * + * 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 * + * of Howard M. Harte. * + * * + * Module Description: * + * TX-0 simulator interface * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +#include "tx0_defs.h" +#include + +typedef struct { + int32 opr; + char *mnemonic; + char *desc; +} OPMAP; + +const OPMAP opmap_orig [] = { + { 0700000, "cll", "Clear the left nine digital positions of the AC" }, + { 0640000, "clr", "Clear the right nine digital positions of the AC" }, + { 0620000, "ios", "In-Out Stop" }, + { 0630000, "hlt", "Halt the computer" }, + { 0607000, "p7h", "Punch holes 1-6 in flexo tape Also punch a 7th hole on tape" }, + { 0606000, "p6h", "Punch holes 1-6 in flexo tape" }, + { 0604000, "pnt", "Print one flexowrite charater" }, + { 0601000, "r1c", "Read one line of flexo tape" }, + { 0603000, "r3c", "Read three lines of flexo tape" }, + { 0602000, "dis", "Intesnsify a point on the scope from x,y in AC" }, + { 0600400, "shr", "Shift the AC right one place" }, + { 0600600, "cyr", "Cycle the AC right one digital position (AC17 -> AC0)" }, + { 0600200, "mlr", "Store the contents of the MBR in the LR" }, + { 0600100, "pen", "Read the light pen flip flops 1 and 2 into AC0 and AC1" }, + { 0600004, "tac", "Insert a one in each digital position of the AC whereever there is a one in the corresponding digital position of the TAC" }, + { 0600040, "com", "Complement every digit in the accumulator" }, + { 0600020, "pad", "Partial add AC to MBR" }, + { 0600010, "cry", "Partial add the 18 digits of the AC to the corresponding 18 digits of the carry" }, + { 0600001, "amb", "Store the contents of the AC in the MBR" }, + { 0600003, "tbr", "Store the contents of the TBR in the MBR" }, + { 0600002, "lmb", "Store the contents of the LR in the MBR" }, +/* Combined Operate Class Commands */ + { 0740000, "cla", "Clear the AC" }, + { 0600031, "cyl", "Cycle the AC left one digital position" }, + { 0740040, "clc", "Clear and complement AC" }, + { 0622000, "dis", "Display (note IOS must be included for in-out cmds)" }, + { 0760000, "ios+cll+clr", "In out stop with AC cleared" }, + { 0627600, "ios+p7h+cyr", "Punch 7 holes and cycle AC right" }, + { 0626600, "ios+p6h+cyr", "Punch 6 holes and cycle AC right" }, + { 0766000, "ios+cll+clr+p6h", "Clear the AC and punch a blank space on tape" }, + { 0624600, "ios+pnt+cyr", "Print and cycle AC right" }, + { 0627021, "ios+p7h+amb+pad", "Punch 7 holes and leave AC cleared" }, + { 0626021, "ios+p6h+amb+pad", "Punch 6 holes and leave AC cleared" }, + { 0624021, "ios+pnt+amb+pad", "Print and leave AC cleared" }, + { 0741000, "cll+clr+ric", "Clear AC and start PETR running (note computer hasn't stopped to wait for information" }, + { 0601031, "ric+amb+pad+cry", "Start PETR running and cycle AC left" }, + { 0601600, "ric+cyr", "Start PETR running and cycle right" }, + { 0763000, "cll+clr+ios+r3c", "Clear AC and read 3 lines of tape" }, + { 0761000, "cll+clr+ios+ric", "Clear AC and read one line of tape" }, + { 0761031, "cll+clr+ios+ric+pad+cry", "Read 1 line of tape and cycle AC left" }, + { 0761600, "cll+clr+ios+ric+cyr", "Read 1 line of tape and cycle right" }, + { 0740004, "cll+clr+tac", "Put contents of TAC in AC" }, + { 0600030, "pad+cry", "Full-add the MBR and AC and leave sum in AC" }, + { 0740022, "cll+clr+lmb+pad", "Clear the AC - store LR contents in memory buffer register add memory buffer to AC - i.e., store live reg. contents in AC (LAC)" }, + { 0600201, "amb+mlr", "Store contents of AC in MBR, store contents of MBR in LR i.e., store contents of AC in LR. (ALR)" }, + { 0600022, "lmb+pad", "Store the contents of LR in MBR, partial add AC and MBR i.e., partial add LR to AC. (LPD)" }, +/* { 0600200, "mlr", "Since MLR alone will ahve a clear MBR, this is really clear LR (LRO)" }, */ + { 0600032, "lmb+pad+cry", "Full-add the LR to the AC (LAD)" }, + { 0740023, "cll+clr+tbr+pad", "Store contents of TBR in AC" }, + { 0000000, NULL, NULL } +}; + +t_stat fprint_sym_orig (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 i, inst, op; + +inst = val[0]; + +/* Instruction decode */ + +op = (inst >> 16) & 3; + +switch (op) { + case 0: + fprintf (of, "sto %06o", inst & 0177777); /* opcode */ + break; + case 1: + fprintf (of, "add %06o", inst & 0177777); /* opcode */ + break; + case 2: + fprintf (of, "trn %06o", inst & 0177777); /* opcode */ + break; + case 3: + for(i=0;opmap_orig[i].opr != 0;i++) { + if(inst == opmap_orig[i].opr) { + fprintf (of, "opr %s (%s)", opmap_orig[i].mnemonic, opmap_orig[i].desc); /* opcode */ + } + } + break; +} + +return SCPE_OK; +} + diff --git a/display/README b/display/README new file mode 100644 index 00000000..7f3b8475 --- /dev/null +++ b/display/README @@ -0,0 +1,166 @@ +$Id: README,v 1.15 2004/02/09 07:20:18 phil Exp $ + +XY Display Simulation +Simulates XY plotting displays used on DEC PDP systems. + + Copyright (c) 2003-2004, Philip L. Budne and Douglas A. Gwyn + + 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 + THE AUTHORS 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 the authors shall + not be used in advertising or otherwise to promote the sale, use or + other dealings in this Software without prior written authorization + from the authors. + +Phil Budne +Douglas A Gwyn +February 5, 2004 + +Designed for use with Bob Supnik's SIMH, but the code should be easily +portable, and usable standalone (see vttest.c for an example). + +Display code is provided for X11 (Unix/VMS) and Win32. +We're not GUI programmers, so the code is PRIMITIVE!! + +Started from VC8E simulator by Douglas W. Jones +(distribution 5, of Feb 4, 1997); + + This PDP8 Emulator was written by Douglas W. Jones at the + University of Iowa. It is distributed as freeware, of + uncertain function and uncertain utility. + +Original phosphor decay constants for Type 30 display from XMame 0.72.1 + +VT11 support GREATLY enhanced (and VT48 support added) and +other general improvements from Douglas A Gwyn. + +In the interest of fair play we have supplied two makefiles (neither +of which is named Makefile nor makefile), one which works under all +flavors of "make" (after necessary editing, in the traditional manner), +and one which functions only under the GNU version of make (sometimes +installed as "gmake", but the default "make" on many systems). We have +not added a third flavor which uses BSD make enhancements, because our +deeply held roots (over 40 combined years of Unix experience by the +authors) demand that things should work on all platforms. Both the +Linux and Windows worlds violate this simple credo (everything works so +long as you use OUR preferred software), and many current users may not +even realize that editing Makefiles used to be de rigeur. Since the +GNU environment is widely available and "gmake" has features that +support automatic configuration for multiple platforms, we have +supplied thr GNU-specific variant with the expectation that many users +will find it more convenient. You can copy or link whichever flavor of +makefile suits your taste to whichever spelling of "makefile" suits +your fancy, or invoke "make" with the -f flag specifying the desired +makefile. + +To compile test programs: +======================== +On Unix: + # edit smakefile to match your environment + make -f smakefile +or + gmake -f gmakefile + +On Win32 (using Cygwin); + make -f gmakefile WIN32=1 + +On Win32 (using MINGW): + # edit smakefile to match your environment + make -f smakefile +or + mingw32-make -f gmakefile WIN32=1 +or + execute build-mingw.bat in a DOS command window + +creates: + + munch: standalone simulation of PDP-1 munching squares; + examines console "test switches" (see next section) + + vt11: sequences through VT11/VS60 simulator test displays; + shows how the diplay-processor simulator can be used + from applications other than PDP-11 simulators + +Console switches: +================ + +Upto 18 simulated console switches, toggled by hitting keys: + +123 456 789 qwe rty uio + +space bar clears all switches. + +Spacewar Switches: +================= + +Key presses for simulated Spacewar control box switches; + +action player + 1 2 +rotate clockwise a k +rotate counter clockwise s l +fire engines d ; +launch torpedo f ' +hyperspace (both at once) as kl + +Light pen: +========= + +The light pen is active when any mouse button is held down. + +Mouse button 1 acts as a "tip switch" for models so equipped (VS60). +The light pen may be dragged while active. + +Too many compile time parameters: +================================ + +Read the comments in display.c for more explanations!! + +DISPLAY_TYPE default display type, one of: + DIS_VR14, DIS_VR17, DIS_VR20, DIS_VR48, DIS_TYPE30, DIS_TYPE340 + selects screen characteristics (phosphor, dimensions). + + Only affects programs which do not make an expicit + display_init() call. + +PIX_SCALE one of RES_FULL, RES_HALF, RES_QUARTER, RES_EIGHTH + selects default display scaling factor. + +PEN_RADIUS default radius of light pen in (scaled) pixels + +MAXELAPSED Upper limit in real microseconds between polls/delays +MINELAPSED Lower limit in real microseconds between polls/delays +MINDELAY Lower limit in real microseconds for attempted delay +MAXDELAY Upper limit in real microseconds for attempted delay +GAINSHIFT delay_check increment/decrement gain factor + +In display system support (x11.c, win32.c); + +PIX_SIZE selects displayed pixel size (default 1) + makes screen larger, useful when display scaled to small size + +Programming interface: +===================== + +see display.h + +Source repository: +================= + +Up-to-date Sources are available by anonymous CVS. +See http://www.ultimate.com/phil/xy/ diff --git a/display/build_mingw.bat b/display/build_mingw.bat new file mode 100644 index 00000000..49222c11 --- /dev/null +++ b/display/build_mingw.bat @@ -0,0 +1,12 @@ +@echo off +rem $Id: build_mingw.bat,v 1.1 2004/01/25 17:48:03 phil Exp $ +rem Compile all test programs using MINGW make and gcc environment +rem +rem If needed, define the path for the MINGW bin directory. +rem (this should already be set if MINGW was installed correctly) +rem +gcc -v 1>NUL 2>NUL +if ERRORLEVEL 1 path C:\MinGW\bin;D:\MinGW\bin;E:\MinGW\bin;%path% +gcc -v 1>NUL 2>NUL +if ERRORLEVEL 1 echo "MinGW Environment Unavailable" +mingw32-make WIN32=1 -f gmakefile %1 %2 %3 %4 diff --git a/display/display.c b/display/display.c new file mode 100644 index 00000000..efbce44b --- /dev/null +++ b/display/display.c @@ -0,0 +1,1053 @@ +/* + * $Id: display.c,v 1.57 2004/02/04 16:59:01 phil Exp $ + * Simulator and host O/S independent XY display simulator + * Phil Budne + * September 2003 + * + * with changes by Douglas A. Gwyn, 21 Jan. 2004 + * + * started from PDP-8/E simulator vc8e.c; + * This PDP8 Emulator was written by Douglas W. Jones at the + * University of Iowa. It is distributed as freeware, of + * uncertain function and uncertain utility. + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne + * + * 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 + * THE AUTHORS 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 the authors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization + * from the authors. + */ + +#include +#include +#include +#include /* for USHRT_MAX */ +#include "ws.h" +#include "display.h" + +/* + * The user may select (at compile time) how big a window is used to + * emulate the display. Using smaller windows saves memory and screen space. + * + * Type 30 has 1024x1024 addressing, but only 512x512 visible points. + * VR14 has only 1024x768 visible points; VR17 has 1024x1024 visible points. + * VT11 supports 4096x4096 addressing, clipping to the lowest 1024x1024 region. + * VR48 has 1024x1024 visible points in the main display area and 128x1024 + * visible points in a menu area on the right-hand side (1152x1024 total). + * VT48 supports 8192x8192 (signed) main-area addressing, clipping to a + * 1024x1024 window which can be located anywhere within that region. + * (XXX -- That is what the VT11/VT48 manuals say; however, evidence suggests + * that the VT11 may actually support 8192x8192 (signed) addressing too.) + */ + +/* Define the default display type (if display_init() not called) */ +#ifndef DISPLAY_TYPE +#define DISPLAY_TYPE DIS_TYPE30 +#endif /* DISPLAY_TYPE not defined */ + +/* select a default resolution if display_init() not called */ +/* XXX keep in struct display? */ +#ifndef PIX_SCALE +#define PIX_SCALE RES_HALF +#endif /* PIX_SCALE not defined */ + +/* select a default light-pen hit radius if display_init() not called */ +#ifndef PEN_RADIUS +#define PEN_RADIUS 4 +#endif /* PEN_RADIUS not defined */ + +/* + * note: displays can have up to two different colors (eg VR20) + * each color can be made up of any number of phosphors + * with different colors and decay characteristics (eg Type 30) + */ + +#define ELEMENTS(X) (sizeof(X)/sizeof(X[0])) + +struct phosphor { + float red, green, blue; + float level; /* decay level (0.5 for half life) */ + float t_level; /* seconds to decay to level */ +}; + +struct color { + struct phosphor *phosphors; + int nphosphors; + int half_life; /* for refresh calc */ +}; + +struct display { + enum display_type type; + char *name; + struct color *color0, *color1; + short xpoints, ypoints; +}; + +/* + * original phosphor constants from Raphael Nabet's XMame 0.72.1 PDP-1 sim. + * not even sure Type30 really used P17 (guess by Daniel P. B. Smith) + */ +static struct phosphor p17[] = { + {0.11, 0.11, 1.0, 0.5, 0.05}, /* fast blue */ + {1.0, 1.0, 0.11, 0.5, 0.20} /* slow yellow/green */ +}; +static struct color color_p17 = { p17, ELEMENTS(p17), 125000 }; + +/* green phosphor for VR14, VR17, VR20 */ +static struct phosphor p29[] = {{0.0260, 1.0, 0.00121, 0.5, 0.025}}; +struct color color_p29 = { p29, ELEMENTS(p29), 25000 }; + +static struct phosphor p40[] = { + /* P40 blue-white spot with yellow-green decay (.045s to 10%?) */ + {0.4, 0.2, 0.924, 0.5, 0.0135}, + {0.5, 0.7, 0.076, 0.5, 0.065} +}; +static struct color color_p40 = { p40, ELEMENTS(p40), 20000 }; + +/* "red" -- until real VR20 phosphor type/number/constants known */ +static struct phosphor pred[] = { {1.0, 0.37, 0.37, 0.5, 0.10} }; +static struct color color_red = { pred, ELEMENTS(pred), 100000 }; + +static struct display displays[] = { + /* + * TX-0 + * + * + * Unknown manufacturer + * + * 12" tube, + * maximum dot size ??? + * 50us point plot time (20,000 points/sec) + * P17 Phosphor??? Two phosphor layers: + * fast blue (.05s half life), and slow green (.2s half life) + * + * + */ + { DIS_TX0, "MIT TX-0", &color_p17, NULL, 512, 512 }, + + + /* + * Type 30 + * PDP-1/4/5/8/9/10 "Precision CRT" display system + * + * Raytheon 16ADP7A CRT? + * web searches for 16ADP7 finds useful information!! + * 16" tube, 14 3/8" square raster + * maximum dot size .015" + * 50us point plot time (20,000 points/sec) + * P17 Phosphor??? Two phosphor layers: + * fast blue (.05s half life), and slow green (.2s half life) + * 360 lb + * 7A at 115+-10V 60Hz + */ + { DIS_TYPE30, "Type 30", &color_p17, NULL, 1024, 1024 }, + + /* + * VR14 + * used w/ GT40/44, AX08, VC8E + * + * Viewable area 6.75" x 9" + * 12" diagonal + * brightness >= 30 fL + * dot size .02" (20 mils) + * settle time: + * full screen 18us to +/-1 spot diameter + * .1" change 1us to +/-.5 spot diameter + * weight 75lb + */ + { DIS_VR14, "VR14", &color_p29, NULL, 1024, 768 }, + + /* + * VR17 + * used w/ GT40/44, AX08, VC8E + * + * Viewable area 9.25" x 9.25" + * 17" diagonal + * dot size .02" (20 mils) + * brightness >= 25 fL + * phosphor: P39 doped for IR light pen use + * light pen: Type 375 + * weight 85lb + */ + { DIS_VR17, "VR17", &color_p29, NULL, 1024, 1024 }, + + /* + * VR20 + * on VC8E + * Two colors!! + */ + { DIS_VR20, "VR20", &color_p29, &color_red, 1024, 1024 }, + + /* + * VR48 + * (on VT48 in VS60) + * from Douglas A. Gwyn 23 Nov. 2003 + * + * Viewable area 12" x 12", plus 1.5" x 12" menu area on right-hand side + * 21" diagonal + * dot size <= .01" (10 mils) + * brightness >= 31 fL + * phosphor: P40 (blue-white fluorescence with yellow-green phosphorescence) + * light pen: Type 377A (with tip switch) + * driving circuitry separate + * (normally under table on which CRT is mounted) + */ + { DIS_VR48, "VR48", &color_p40, NULL, 1024+VR48_GUTTER+128, 1024 }, + + /* + * Type 340 Display system + * on PDP-4/6/7/9/10 + * + * 1024x1024 + * 9 3/8" raster (.01" dot pitch) + * 0,0 at lower left + * 8 intensity levels + */ + { DIS_TYPE340, "Type 340", &color_p17, NULL, 1024, 1024 } +}; + +/* + * Unit time (in microseconds) used to store display point time to + * live at current aging level. If this is too small, delay values + * cannot fit in an unsigned short. If it is too large all pixels + * will age at once. Perhaps a suitable value should be calculated at + * run time? When display_init() calculates refresh_interval it + * sanity checks for both cases. + */ +#define DELAY_UNIT 250 + +/* levels to display in first half-life; determines refresh rate */ +#ifndef LEVELS_PER_HALFLIFE +#define LEVELS_PER_HALFLIFE 4 +#endif + +/* after 5 half lives (.5**5) remaining intensity is 3% of original */ +#ifndef HALF_LIVES_TO_DISPLAY +#define HALF_LIVES_TO_DISPLAY 5 +#endif + +/* + * refresh_rate is number of times per (simulated) second a pixel is + * aged to next lowest intensity level. + * + * refresh_rate = ((1e6*LEVELS_PER_HALFLIFE)/PHOSPHOR_HALF_LIFE) + * refresh_interval = 1e6/DELAY_UNIT/refresh_rate + * = PHOSPHOR_HALF_LIFE/LEVELS_PER_HALF_LIFE + * intensities = (HALF_LIVES_TO_DISPLAY*PHOSPHOR_HALF_LIFE)/refresh_interval + * = HALF_LIVES_TO_DISPLAY*LEVELS_PER_HALFLIFE + * + * See also comments on display_age() + * + * Try to keep LEVELS_PER_HALFLIFE*HALF_LIVES_TO_DISPLAY*NLEVELS <= 192 + * to run on 8-bit (256 color) displays! + */ + +/* + * number of aging periods to display a point for + */ +#define NTTL (HALF_LIVES_TO_DISPLAY*LEVELS_PER_HALFLIFE) + +/* + * maximum (initial) TTL for a point. + * TTL's are stored 1-based + * (a stored TTL of zero means the point is off) + */ +#define MAXTTL NTTL + +/* + * number of drawing intensity levels + */ +#define NLEVELS (DISPLAY_INT_MAX-DISPLAY_INT_MIN+1) + +#define MAXLEVEL (NLEVELS-1) + +/* + * Display Device Implementation + */ + +/* + * Each point on the display is represented by a "struct point". When + * a point isn't dark (intensity > 0), it is linked into a circular, + * doubly linked delta queue (a priority queue where "delay" + * represents the time difference from the previous entry (if any) in + * the queue. + * + * All points are aged refresh_rate times/second, each time moved to the + * next (logarithmically) lower intensity level. When display_age() is + * called, only the entries which have expired are processed. Calling + * display_age() often allows spreading out the workload. + * + * An alternative would be to have intensity levels represent linear + * decreases in intensity, and have the decay time at each level change. + * Inverting the decay function for a multi-component phosphor may be + * tricky, and the two different colors would need different time tables. + * Furthermore, it would require finding the correct location in the + * queue when adding a point (currently only need to add points at end) + */ + +/* + * 12 bytes/entry on 32-bit system when REFRESH_RATE > 15 + * (requires 3MB for 512x512 display). + */ + +typedef unsigned short delay_t; +#define DELAY_T_MAX USHRT_MAX + +struct point { + struct point *next; /* next entry in queue */ + struct point *prev; /* prev entry in queue */ + delay_t delay; /* delta T in DELAY_UNITs */ + unsigned char ttl; /* zero means off, not linked in */ + unsigned char level : 7; /* intensity level */ + unsigned char color : 1; /* for VR20 (two colors) */ +}; + +static struct point *points; /* allocated array of points */ +static struct point _head; +#define head (&_head) + +/* + * time span of all entries in queue + * should never exceed refresh_interval + * (should be possible to make this a delay_t) + */ +static long queue_interval; + +/* convert X,Y to a "struct point *" */ +#define P(X,Y) (points + (X) + ((Y)*(size_t)xpixels)) + +/* convert "struct point *" to X and Y */ +#define X(P) (((P) - points) % xpixels) +#define Y(P) (((P) - points) / xpixels) + +static int initialized = 0; + +/* + * global set by O/S display level to indicate "light pen tip switch activated" + * (This is used only by the VS60 emulation, also by vttest to change patterns) + */ +unsigned char display_lp_sw = 0; + +/* + * global set by DR11-C simulation when DR device enabled; deactivates + * light pen and instead reports mouse coordinates as Talos digitizer + * data via DR11-C + */ +unsigned char display_tablet = 0; + +/* + * can be changed with display_lp_radius() + */ +static long scaled_pen_radius_squared; + +/* run-time -- set by display_init() */ +static int xpoints, ypoints; +static int xpixels, ypixels; +static int refresh_rate; +static int refresh_interval; +static int ncolors; +static enum display_type display_type; +static int scale; + +/* + * relative brightness for each display level + * (all but last must be less than 1.0) + */ +static float level_scale[NLEVELS]; + +/* + * table of pointer to window system "colors" + * for painting each age level, intensity level and beam color + */ +void *colors[2][NLEVELS][NTTL]; + +void +display_lp_radius(int r) +{ + r /= scale; + scaled_pen_radius_squared = r * r; +} + +/* + * from display_age and display_point + * since all points age at the same rate, + * only adds points at end of list. + */ +static void +queue_point(struct point *p) +{ + int d; + + d = refresh_interval - queue_interval; + queue_interval += d; + /* queue_interval should now be == refresh_interval */ + +#ifdef PARANOIA + if (p->ttl == 0 || p->ttl > MAXTTL) + printf("queuing %d,%d level %d!\n", X(p), Y(p), p->level); + if (d > DELAY_T_MAX) + printf("queuing %d,%d delay %d!\n", X(p), Y(p), d); + if (queue_interval > DELAY_T_MAX) + printf("queue_interval (%d) > DELAY_T_MAX (%d)\n", + (int)queue_interval, DELAY_T_MAX); +#endif /* PARANOIA defined */ + + p->next = head; + p->prev = head->prev; + + head->prev->next = p; + head->prev = p; + + p->delay = d; +} + +/* + * here to to dynamically adjust interval for examination + * of elapsed vs. simulated time, and fritter away + * any extra wall-clock time without eating CPU + */ + +/* + * more parameters! + */ + +/* + * upper bound for elapsed time between elapsed time checks. + * if more than MAXELAPSED microseconds elapsed while simulating + * delay_check simulated microseconds, decrease delay_check. + */ +#define MAXELAPSED 100000 /* 10Hz */ + +/* + * lower bound for elapsed time between elapsed time checks. + * if fewer than MINELAPSED microseconds elapsed while simulating + * delay_check simulated microseconds, increase delay_check. + */ +#define MINELAPSED 50000 /* 20Hz */ + +/* + * upper bound for delay (sleep/poll). + * If difference between elapsed time and simulated time is + * larger than MAXDELAY microseconds, decrease delay_check. + * + * since delay is elapsed time - simulated time, MAXDELAY + * should be <= MAXELAPSED + */ +#ifndef MAXDELAY +#define MAXDELAY 100000 /* 100ms */ +#endif /* MAXDELAY not defined */ + +/* + * lower bound for delay (sleep/poll). + * If difference between elapsed time and simulated time is + * smaller than MINDELAY microseconds, increase delay_check. + * + * since delay is elapsed time - simulated time, MINDELAY + * should be <= MINELAPSED + */ +#ifndef MINDELAY +#define MINDELAY 50000 /* 50ms */ +#endif /* MINDELAY not defined */ + +/* + * Initial amount of simulated time to elapse before polling. + * Value is very low to ensure polling occurs on slow systems. + * Fast systems should ramp up quickly. + */ +#ifndef INITIAL_DELAY_CHECK +#define INITIAL_DELAY_CHECK 1000 /* 1ms */ +#endif /* INITIAL_DELAY_CHECK */ + +/* + * gain factor (2**-GAINSHIFT) for adjustment of adjustment + * of delay_check + */ +#ifndef GAINSHIFT +#define GAINSHIFT 3 /* gain=0.125 (12.5%) */ +#endif /* GAINSHIFT not defined */ + +static void +display_delay(int t, int slowdown) +{ + /* how often (in simulated us) to poll/check for delay */ + static unsigned long delay_check = INITIAL_DELAY_CHECK; + + /* accumulated simulated time */ + static unsigned long sim_time = 0; + unsigned long elapsed; + long delay; + + sim_time += t; + if (sim_time < delay_check) + return; + + elapsed = os_elapsed(); /* read and reset elapsed timer */ + if (elapsed == ~0L) { /* first time thru? */ + slowdown = 0; /* no adjustments */ + elapsed = sim_time; + } + + /* + * get delta between elapsed (real) time, and simulated time. + * if simulated time running faster, we need to slow things down (delay) + */ + if (slowdown) + delay = sim_time - elapsed; + else + delay = 0; /* just poll */ + +#ifdef DEBUG_DELAY2 + printf("sim %d elapsed %d delay %d\r\n", sim_time, elapsed, delay); +#endif + + /* + * Try to keep the elapsed (real world) time between checks for + * delay (and poll for window system events) bounded between + * MAXELAPSED and MINELAPSED. Also tries to keep + * delay/poll time bounded between MAXDELAY and MINDELAY -- large + * delays make the simulation spastic, while very small ones are + * inefficient (too many system calls) and tend to be inaccurate + * (operating systems have a certain granularity for time + * measurement, and when you try to sleep/poll for very short + * amounts of time, the noise will dominate). + * + * delay_check period may be adjusted often, and oscillate. There + * is no single "right value", the important things are to keep + * the delay time and max poll intervals bounded, and responsive + * to system load. + */ + if (elapsed > MAXELAPSED || delay > MAXDELAY) { + /* too much elapsed time passed, or delay too large; shrink interval */ + if (delay_check > 1) { + delay_check -= delay_check>>GAINSHIFT; +#ifdef DEBUG_DELAY + printf("reduced period to %d\r\n", delay_check); +#endif /* DEBUG_DELAY defined */ + } + } + else if (elapsed < MINELAPSED || slowdown && delay < MINDELAY) { + /* too little elapsed time passed, or delta very small */ + int gain = delay_check>>GAINSHIFT; + if (gain == 0) + gain = 1; /* make sure some change made! */ + delay_check += gain; +#ifdef DEBUG_DELAY + printf("increased period to %d\r\n", delay_check); +#endif /* DEBUG_DELAY defined */ + } + if (delay < 0) + delay = 0; + /* else if delay < MINDELAY, clamp at MINDELAY??? */ + + /* poll for window system events and/or delay */ + ws_poll(NULL, delay); + + sim_time = 0; /* reset simulated time clock */ + + /* + * delay (poll/sleep) time included in next "elapsed" period + * (clock not reset after a delay) + */ +} /* display_delay */ + +/* + * here periodically from simulator to age pixels. + * + * calling often with small values will age a few pixels at a time, + * and assist with graceful aging of display, and pixel aging. + * + * values should be smaller than refresh_interval! + * + * returns true if anything on screen changed. + */ + +int +display_age(int t, /* simulated us since last call */ + int slowdown) /* slowdown to simulated speed */ +{ + struct point *p; + static int elapsed = 0; + int changed; + + if (!initialized && !display_init(DISPLAY_TYPE, PIX_SCALE)) + return 0; + + display_delay(t, slowdown); + + changed = 0; + + elapsed += t; + if (elapsed < DELAY_UNIT) + return 0; + + t = elapsed / DELAY_UNIT; + elapsed %= DELAY_UNIT; + + while ((p = head->next) != head) { + int x, y; + + /* look at oldest entry */ + if (p->delay > t) { /* further than our reach? */ + p->delay -= t; /* update head */ + queue_interval -= t; /* update span */ + break; /* quit */ + } + + x = X(p); + y = Y(p); +#ifdef PARANOIA + if (p->ttl == 0) + printf("BUG: age %d,%d ttl zero\n", x, y); +#endif /* PARANOIA defined */ + + /* dequeue point */ + p->prev->next = p->next; + p->next->prev = p->prev; + + t -= p->delay; /* lessen our reach */ + queue_interval -= p->delay; /* update queue span */ + + ws_display_point(x, y, colors[p->color][p->level][--p->ttl]); + changed = 1; + + /* queue it back up, unless we just turned it off! */ + if (p->ttl > 0) + queue_point(p); + } + return changed; +} /* display_age */ + +/* here from window system */ +void +display_repaint(void) { + struct point *p; + int x, y; + /* + * bottom to top, left to right. + */ + for (p = points, y = 0; y < ypixels; y++) + for (x = 0; x < xpixels; p++, x++) + if (p->ttl) + ws_display_point(x, y, colors[p->color][p->level][p->ttl-1]); + ws_sync(); +} + +/* (0,0) is lower left */ +static int +intensify(int x, /* 0..xpixels */ + int y, /* 0..ypixels */ + int level, /* 0..MAXLEVEL */ + int color) /* for VR20! 0 or 1 */ +{ + struct point *p; + int bleed; + + if (x < 0 || x >= xpixels || y < 0 || y >= ypixels) + return 0; /* limit to display */ + + p = P(x,y); + if (p->ttl) { /* currently lit? */ +#ifdef LOUD + printf("%d,%d old level %d ttl %d new %d\r\n", + x, y, p->level, p->ttl, level); +#endif /* LOUD defined */ + + /* unlink from delta queue */ + p->prev->next = p->next; + + if (p->next == head) + queue_interval -= p->delay; + else + p->next->delay += p->delay; + p->next->prev = p->prev; + } + + bleed = 0; /* no bleeding for now */ + + /* EXP: doesn't work... yet */ + /* if "recently" drawn, same or brighter, same color, make even brighter */ + if (p->ttl >= MAXTTL*2/3 && level >= p->level && p->color == color && + level < MAXLEVEL) + level++; + + /* + * this allows a dim beam to suck light out of + * a recently drawn bright spot!! + */ + if (p->ttl != MAXTTL || p->level != level || p->color != color) { + p->ttl = MAXTTL; + p->level = level; + p->color = color; /* save color even if monochrome */ + ws_display_point(x, y, colors[p->color][p->level][p->ttl-1]); + } + + queue_point(p); /* put at end of list */ + return bleed; +} + +int +display_point(int x, /* 0..xpixels (unscaled) */ + int y, /* 0..ypixels (unscaled) */ + int level, /* DISPLAY_INT_xxx */ + int color) /* for VR20! 0 or 1 */ +{ + long lx, ly; + + if (!initialized && !display_init(DISPLAY_TYPE, PIX_SCALE)) + return 0; + + /* scale x and y to the displayed number of pixels */ + /* handle common cases quickly */ + if (scale > 1) { + if (scale == 2) { + x >>= 1; + y >>= 1; + } + else { + x /= scale; + y /= scale; + } + } + +#if DISPLAY_INT_MIN > 0 + level -= DISPLAY_INT_MIN; /* make zero based */ +#endif + intensify(x, y, level, color); + /* no bleeding for now (used to recurse for neighbor points) */ + + if (ws_lp_x == -1 || ws_lp_y == -1) + return 0; + + lx = x - ws_lp_x; + ly = y - ws_lp_y; + return lx*lx + ly*ly <= scaled_pen_radius_squared; +} /* display_point */ + +/* + * calculate decay color table for a phosphor mixture + * must be called AFTER refresh_rate initialized! + */ +static void +phosphor_init(struct phosphor *phosphors, int nphosphors, int color) +{ + int ttl; + + /* for each display ttl level; newest to oldest */ + for (ttl = NTTL-1; ttl > 0; ttl--) { + struct phosphor *pp; + double rr, rg, rb; /* real values */ + + /* fractional seconds */ + double t = ((double)(NTTL-1-ttl))/refresh_rate; + + int ilevel; /* intensity levels */ + int p; + + /* sum over all phosphors in mixture */ + rr = rg = rb = 0.0; + for (pp = phosphors, p = 0; p < nphosphors; pp++, p++) { + double decay = pow(pp->level, t/pp->t_level); + rr += decay * pp->red; + rg += decay * pp->green; + rb += decay * pp->blue; + } + + /* scale for brightness for each intensity level */ + for (ilevel = MAXLEVEL; ilevel >= 0; ilevel--) { + int r, g, b; + void *cp; + + /* + * convert to 16-bit integer; clamp at 16 bits. + * this allows the sum of brightness factors across phosphors + * for each of R G and B to be greater than 1.0 + */ + + r = rr * level_scale[ilevel] * 0xffff; + if (r > 0xffff) r = 0xffff; + + g = rg * level_scale[ilevel] * 0xffff; + if (g > 0xffff) g = 0xffff; + + b = rb * level_scale[ilevel] * 0xffff; + if (b > 0xffff) b = 0xffff; + + cp = ws_color_rgb(r, g, b); + if (!cp) { /* allocation failed? */ + if (ttl == MAXTTL-1) { /* brand new */ + if (ilevel == MAXLEVEL) /* highest intensity? */ + cp = ws_color_white(); /* use white */ + else + cp = colors[color][ilevel+1][ttl]; /* use next lvl */ + } /* brand new */ + else if (r + g + b >= 0xffff*3/3) /* light-ish? */ + cp = colors[color][ilevel][ttl+1]; /* use previous TTL */ + else + cp = ws_color_black(); + } + colors[color][ilevel][ttl] = cp; + } /* for each intensity level */ + } /* for each TTL */ +} /* phosphor_init */ + +static struct display * +find_type(enum display_type type) +{ + int i; + struct display *dp; + for (i = 0, dp = displays; i < ELEMENTS(displays); i++, dp++) + if (dp->type == type) + return dp; + return NULL; +} + +int +display_init(enum display_type type, int sf) +{ + static int init_failed = 0; + struct display *dp; + int half_life; + int i; + + if (initialized) { + /* cannot change type once started */ + /* XXX say something???? */ + return type == display_type; + } + + if (init_failed) + return 0; /* avoid thrashing */ + + init_failed = 1; /* assume the worst */ + dp = find_type(type); + if (!dp) { + fprintf(stderr, "Unknown display type %d\r\n", (int)type); + goto failed; + } + + /* Initialize display list */ + head->next = head->prev = head; + + display_type = type; + scale = sf; + + xpoints = dp->xpoints; + ypoints = dp->ypoints; + + /* increase scale factor if won't fit on desktop? */ + xpixels = xpoints / scale; + ypixels = ypoints / scale; + + /* set default pen radius now that scale is set */ + display_lp_radius(PEN_RADIUS); + + ncolors = 1; + /* + * use function to calculate from looking at avg (max?) + * of phosphor half lives??? + */ +#define COLOR_HALF_LIFE(C) ((C)->half_life) + + half_life = COLOR_HALF_LIFE(dp->color0); + if (dp->color1) { + if (dp->color1->half_life > half_life) + half_life = COLOR_HALF_LIFE(dp->color1); + ncolors++; + } + + /* before phosphor_init; */ + refresh_rate = (1000000*LEVELS_PER_HALFLIFE)/half_life; + refresh_interval = 1000000/DELAY_UNIT/refresh_rate; + + /* + * sanity check refresh_interval + * calculating/selecting DELAY_UNIT at runtime might avoid this! + */ + + /* must be non-zero; interval of 1 means all pixels will age at once! */ + if (refresh_interval < 1) { + /* decrease DELAY_UNIT? */ + fprintf(stderr, "NOTE! refresh_interval too small: %d\r\n", + refresh_interval); + + /* dunno if this is a good idea, but might be better than dying */ + refresh_interval = 1; + } + + /* point lifetime in DELAY_UNITs will not fit in p->delay field! */ + if (refresh_interval > DELAY_T_MAX) { + /* increase DELAY_UNIT? */ + fprintf(stderr, "bad refresh_interval %d > DELAY_T_MAX %d\r\n", + refresh_interval, DELAY_T_MAX); + goto failed; + } + + /* + * before phosphor_init; + * set up relative brightness of display intensity levels + * (could differ for different hardware) + * + * linear for now. boost factor insures low intensities are visible + */ +#define BOOST 5 + for (i = 0; i < NLEVELS; i++) + level_scale[i] = ((float)i+1+BOOST)/(NLEVELS+BOOST); + + points = (struct point *)calloc((size_t)xpixels, + ypixels * sizeof(struct point)); + if (!points) + goto failed; + + if (!ws_init(dp->name, xpixels, ypixels, ncolors)) + goto failed; + + phosphor_init(dp->color0->phosphors, dp->color0->nphosphors, 0); + + if (dp->color1) + phosphor_init(dp->color1->phosphors, dp->color1->nphosphors, 1); + + initialized = 1; + init_failed = 0; /* hey, we made it! */ + return 1; + + failed: + fprintf(stderr, "Display initialization failed\r\n"); + return 0; +} + +void +display_reset(void) +{ + /* XXX tear down window? just clear it? */ +} + +void +display_sync(void) +{ + ws_sync(); +} + +void +display_beep(void) +{ + ws_beep(); +} + +int +display_xpoints(void) +{ + return xpoints; +} + +int +display_ypoints(void) +{ + return ypoints; +} + +int +display_scale(void) +{ + return scale; +} + +/* + * handle keyboard events + * + * data switches; 18 -- enough for PDP-1/4/7/9/15 (for munching squares!) + * 123 456 789 qwe rty uio + * bit toggled on key up + * all cleared on space + * + * spacewar switches; bit high as long as key down + * asdf kl;' + * just where PDP-1 spacewar expects them! + * key mappings same as MIT Media Lab Java PDP-1 simulator + * + */ +unsigned long spacewar_switches = 0; + +/* here from window system */ +void +display_keydown(int k) +{ + switch (k) { + case 'f': case 'F': spacewar_switches |= 01; break; /* torpedos */ + case 'd': case 'D': spacewar_switches |= 02; break; /* engines */ + case 'a': case 'A': spacewar_switches |= 04; break; /* rotate R */ + case 's': case 'S': spacewar_switches |= 010; break; /* rotate L */ + case '\'': case '"': spacewar_switches |= 040000; break; /* torpedos */ + case ';': case ':': spacewar_switches |= 0100000; break; /* engines */ + case 'k': case 'K': spacewar_switches |= 0200000; break; /* rotate R */ + case 'l': case 'L': spacewar_switches |= 0400000; break; /* rotate L */ + default: return; + } +} + +/* here from window system */ +void +display_keyup(int k) +{ + unsigned long test_switches = cpu_get_switches(); + + /* fetch console switches from simulator? */ + switch (k) { + case 'f': case 'F': spacewar_switches &= ~01; return; + case 'd': case 'D': spacewar_switches &= ~02; return; + case 'a': case 'A': spacewar_switches &= ~04; return; + case 's': case 'S': spacewar_switches &= ~010; return; + + case '\'': case '"': spacewar_switches &= ~040000; return; + case ';': case ':': spacewar_switches &= ~0100000; return; + case 'k': case 'K': spacewar_switches &= ~0200000; return; + case 'l': case 'L': spacewar_switches &= ~0400000; return; + + case '1': test_switches ^= 1<<17; break; + case '2': test_switches ^= 1<<16; break; + case '3': test_switches ^= 1<<15; break; + + case '4': test_switches ^= 1<<14; break; + case '5': test_switches ^= 1<<13; break; + case '6': test_switches ^= 1<<12; break; + + case '7': test_switches ^= 1<<11; break; + case '8': test_switches ^= 1<<10; break; + case '9': test_switches ^= 1<<9; break; + + case 'q': case 'Q': test_switches ^= 1<<8; break; + case 'w': case 'W': test_switches ^= 1<<7; break; + case 'e': case 'E': test_switches ^= 1<<6; break; + + case 'r': case 'R': test_switches ^= 1<<5; break; + case 't': case 'T': test_switches ^= 1<<4; break; + case 'y': case 'Y': test_switches ^= 1<<3; break; + + case 'u': case 'U': test_switches ^= 1<<2; break; + case 'i': case 'I': test_switches ^= 1<<1; break; + case 'o': case 'O': test_switches ^= 1; break; + + case ' ': test_switches = 0; break; + default: return; + } + cpu_set_switches(test_switches); +} diff --git a/display/display.h b/display/display.h new file mode 100644 index 00000000..94616b8f --- /dev/null +++ b/display/display.h @@ -0,0 +1,143 @@ +/* + * $Id: display.h,v 1.13 2004/01/24 08:34:33 phil Exp $ + * interface to O/S independent layer of XY display simulator + * Phil Budne + * September 2003 + * + * Changes from Douglas A. Gwyn, Jan 12, 2004 + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne + * + * 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 + * THE AUTHORS 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 the authors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization + * from the authors. + */ + +/* + * known display types + */ +enum display_type { + DIS_VR14 = 14, + DIS_VR17 = 17, + DIS_VR20 = 20, + DIS_TYPE30 = 30, + DIS_TX0 = 33, + DIS_VR48 = 48, + DIS_TYPE340 = 340 +}; + +/* + * display scale factors + */ +#define RES_FULL 1 +#define RES_HALF 2 +#define RES_QUARTER 4 +#define RES_EIGHTH 8 + +/* + * must be called before first call to display_age() + * (but called implicitly by display_point()) + */ +extern int display_init(enum display_type, int scale); + +/* return size of virtual display */ +extern int display_xpoints(void); +extern int display_ypoints(void); + +/* virtual points between display and menu sections */ +#define VR48_GUTTER 8 /* just a guess */ + +/* conversion factor from virtual points and displayed pixels */ +extern int display_scale(void); + +/* + * simulate passage of time; first argument is simulated microseconds elapsed, + * second argument is flag to slow down simulated speed + * see comments in display.c for why you should call it often!! + * Under X11 polls for window events!! + */ +extern int display_age(int,int); + +/* + * display intensity levels. + * always at least 8 (for VT11/VS60) -- may be mapped internally + */ +#define DISPLAY_INT_MAX 7 +#define DISPLAY_INT_MIN 0 /* lowest "on" level */ + +/* + * plot a point; argumen ts are x, y, intensity, color (0/1) + * returns true if light pen active (mouse button down) + * at (or very near) this location. + * + * Display initialized on first call. + */ +extern int display_point(int,int,int,int); + +/* + * force window system to output bits to screen; + * call after adding points, or aging the screen + */ +extern void display_sync(void); + +/* + * currently a noop + */ +extern void display_reset(void); + +/* + * ring the bell + */ +extern void display_beep(void); + +/* + * Set light-pen radius; maximum radius in display coordinates + * from a "lit" location that the light pen will see. + */ +extern void display_lp_radius(int); + +/* + * set by simulated spacewar switch box switches + * 18 bits (only high 4 and low 4 used) + */ +extern unsigned long spacewar_switches; + +/* + * light pen "tip switch" activated (for VS60 emulation etc.) + * should only be set from "driver" (window system layer) + */ +extern unsigned char display_lp_sw; + +/* + * deactivates light pen + * (SIMH DR11-C simulation when initialized sets this and + * then reports mouse coordinates as Talos digitizer data) + */ +extern unsigned char display_tablet; + +/* + * users of this library are expected to provide these calls. + * simulator will set 18 simulated switches. + */ +extern unsigned long cpu_get_switches(void); /* get current switch state */ +extern void cpu_set_switches(unsigned long); /* set switches */ diff --git a/display/gmakefile b/display/gmakefile new file mode 100644 index 00000000..4ef7b28e --- /dev/null +++ b/display/gmakefile @@ -0,0 +1,77 @@ +# $Id: gmakefile,v 1.17 2004/01/24 08:31:56 phil Exp - revised by DAG + +# (GNU) Makefile for test programs under Unix/X11 and Win32 +# +# Unix: +# edit Unix defs to fit your compiler/library environment, then +# gmake -f gmakefile +# or if GNU make is the default: +# make -f gmakefile +# +# Win32 (Cygwin) +# make WIN32=1 +# +# Win32 (MINGW): +# mingw32-make -f gmakefile WIN32=1 + +DISP_DEFS=-DTEST_DIS=DIS_VR48 -DTEST_RES=RES_HALF # -DDEBUG_VT11 + +ifeq ($(WIN32),) +#Unix environments +X11BASE=/usr/X11R6 +X11LIBDIR=$(X11BASE)/lib +X11INCDIR=$(X11BASE)/include +LIBS=-L$(X11LIBDIR) -lXt -lX11 -lm +OSFLAGS=-I$(X11INCDIR) +DRIVER=x11.o +EXT= +else +#Win32 environments +LIBS=-lgdi32 +OSFLAGS= +DRIVER=win32.o +EXT=.exe +endif + +#PROF=-g # -pg +OPT=-O2 +CFLAGS=$(OPT) $(PROF) $(OSFLAGS) $(DISP_DEFS) +CC=gcc -Wunused +LDFLAGS=$(PROF) + +ALL= munch$(EXT) vt11$(EXT) +ALL: $(ALL) + +# munching squares; see README file for +# how to use console switches + +MUNCH=$(DRIVER) display.o test.o +munch$(EXT): $(MUNCH) + $(CC) $(LDFLAGS) -o munch$(EXT) $(MUNCH) $(LIBS) + +VT11=$(DRIVER) vt11.o vttest.o display.o +vt11$(EXT): $(VT11) + $(CC) $(LDFLAGS) -o vt11$(EXT) $(VT11) $(LIBS) + +display.o: display.h ws.h +vt11.o: display.h vt11.h +x11.o: ws.h display.h +win32.o: ws.h +test.o: display.h vt11.h +vttest.o: display.h vt11.h vtmacs.h + +clean: +ifeq ($(WIN32),) + rm -f *.o *~ .#* +else + if exist *.o del /q *.o + if exist *~ del /q *~ + if exist .#* del /q .#* +endif + +clobber: clean +ifeq ($(WIN32),) + rm -f $(ALL) +else + if exist *.exe del /q *.exe +endif diff --git a/display/smakefile b/display/smakefile new file mode 100644 index 00000000..0956a917 --- /dev/null +++ b/display/smakefile @@ -0,0 +1,75 @@ +# $Id: smakefile,v 1.17 2004/01/24 08:31:56 phil Exp - revised by DAG + +# Makefile for test programs (standard Unix "make" version) + +# Unix: +# comment out Windows defs, uncomment Unix defs, +# edit Unix defs to fit your compiler/library environment, then +# (g)make +# +# Win32 (Cygwin) +# comment out Unix defs, uncomment Windows defs, then +# make +# +# Win32 (MINGW) +# comment out Unix defs, uncomment Windows defs, then +# mingw32-make + +DISP_DEFS=-DTEST_DIS=DIS_VR48 -DTEST_RES=RES_HALF # -DDEBUG_VT11 + +#Unix environments +CC=cc # gcc -Wunused +#X11BASE=/usr/X11R6 +#X11LIBDIR=$(X11BASE)/lib +#X11INCDIR=$(X11BASE)/include +LIBS=-lXt -lX11 -lm # -L$(X11LIBDIR) +OSFLAGS=-I$(X11INCDIR) +DRIVER=x11.o +EXT= +PROF=-g # -pg +OPT=-O # -O2 +CFLAGS=$(OPT) $(PROF) $(OSFLAGS) $(DISP_DEFS) +CC=cc # gcc -Wunused +LDFLAGS=$(PROF) + +##Win32 environments +#LIBS=-lgdi32 +#OSFLAGS= +#DRIVER=win32.o +#EXT=.exe +#PROF=-g # -pg +#OPT=-O2 +#CFLAGS=$(OPT) $(PROF) $(OSFLAGS) $(DISP_DEFS) +#CC=gcc -Wunused +LDFLAGS=$(PROF) + +ALL= munch$(EXT) vt11$(EXT) +ALL: $(ALL) + +# munching squares; see README file for +# how to use console switches + +MUNCH=$(DRIVER) display.o test.o +munch$(EXT): $(MUNCH) + $(CC) $(LDFLAGS) -o munch$(EXT) $(MUNCH) $(LIBS) + +VT11=$(DRIVER) vt11.o vttest.o display.o +vt11$(EXT): $(VT11) + $(CC) $(LDFLAGS) -o vt11$(EXT) $(VT11) $(LIBS) + +display.o: display.h ws.h +vt11.o: display.h vt11.h +x11.o: ws.h display.h +win32.o: ws.h +test.o: display.h vt11.h +vttest.o: display.h vt11.h vtmacs.h + +clean: + rm -f *.o *~ .#* # Unix +# if exist *.o del /q *.o # Win32 +# if exist *~ del /q *~ # Win32 +# if exist .#* del /q .#* # Win32 + +clobber: clean + rm -f $(ALL) # Unix +# if exist *.exe del /q *.exe # Win32 diff --git a/display/test.c b/display/test.c new file mode 100644 index 00000000..5b5b6438 --- /dev/null +++ b/display/test.c @@ -0,0 +1,192 @@ +/* + * $Id: test.c,v 1.23 2004/02/07 06:31:20 phil Exp $ + * XY Display simulator test program (PDP-1 Munching Squares) + * Phil Budne + * September 2003 + * + * Updates from Douglas A. Gwyn, 12 Jan. 2004 + * + * With thanks to Daniel Smith for his web page: + * http://world.std.com/~dpbsmith/munch.html + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne + * + * 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 + * THE AUTHORS 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 the authors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization + * from the authors. + */ + +#ifndef TEST_DIS +#define TEST_DIS DIS_TYPE30 +#endif + +#ifndef TEST_RES +#define TEST_RES RES_HALF +#endif + +#include +#include + +#ifndef EXIT_FAILURE +/* SunOS4 doesn't define this */ +#define EXIT_FAILURE 1 +#endif + +#include "display.h" + +static unsigned long test_switches = 0; + +/* called from display code: */ +unsigned long +cpu_get_switches(void) { + return test_switches; +} + +/* called from display code: */ +void +cpu_set_switches(bits) + unsigned long bits; +{ + printf("switches: %06lo\n", bits); + test_switches = bits; +} + +void +munch(void) { + static long us = 0; + static long io = 0, v = 0; + long ac; + int x, y; + + ac = test_switches; + ac += v; /* add v */ + if (ac & ~0777777) { + ac++; + ac &= 0777777; + } + v = ac; /* dac v */ + + ac <<= 9; /* rcl 9s */ + io <<= 9; + io |= ac>>18; + ac &= 0777777; + ac |= io>>18; + io &= 0777777; + + ac ^= v; /* xor v */ + + /* convert +/-512 one's complement to 0..1022, origin in lower left */ + y = (io >> 8) & 01777; /* hi 10 */ + if (y & 01000) + y ^= 01000; + else + y += 511; + + x = (ac >> 8) & 01777; /* hi 10 */ + if (x & 01000) /* negative */ + x ^= 01000; + else + x += 511; + + if (display_point(x, y, DISPLAY_INT_MAX, 0)) + printf("light pen hit at (%d,%d)\n", x, y); + +/*#define US 100000 /* 100ms (10/sec) */ +/*#define US 50000 /* 50ms (20/sec) */ +/*#define US 20000 /* 20ms (50/sec) */ +/*#define US 10000 /* 10ms (100/sec) */ +#define US 0 + us += 50; /* 10 5us PDP-1 memory cycles */ + if (us >= US) { + display_age(us, 1); + us = 0; + } + display_sync(); /* XXX push down */ +} + +#ifdef T2 +/* display all window system level intensities; + * must be compiled with -DINTENSITIES= -DT2 + */ +void +t2(void) { + int x, y; + + display_init(TEST_DIS, TEST_RES); + for (x = INTENSITIES-1; x >= 0; x--) { + for (y = 0; y < 20; y++) { + ws_display_point(x*4, y, x, 0); + ws_display_point(x*4+1, y, x, 0); + ws_display_point(x*4+2, y, x, 0); + ws_display_point(x*4+3, y, x, 0); + } + display_sync(); + } + fflush(stdout); + for (;;) + /* wait */ ; +} +#endif + +#ifdef T3 +/* display all "user" level intensities; + * must be compiled with -DINTENSITIES= -DT3 + * + * skip every other virtual point on both axes + * default scaling maps adjacent pixels and + * causes re-intensification! + */ +void +t3(void) { + int x, y; + + display_init(TEST_DIS, TEST_RES); + for (x = DISPLAY_INT_MAX; x >= 0; x--) { + for (y = 0; y < 20; y++) { + display_point(x*2, y*2, x, 0); + } + display_sync(); + } + fflush(stdout); + for (;;) + /* wait */ ; +} +#endif + +int +main(void) { + if (!display_init(TEST_DIS, TEST_RES)) + exit(EXIT_FAILURE); + + cpu_set_switches(04000UL); /* classic starting value */ + for (;;) { +#ifdef T2 + t2(); +#endif +#ifdef T3 + t3(); +#endif + munch(); + } + /*NOTREACHED*/ +} diff --git a/display/type340.c b/display/type340.c new file mode 100644 index 00000000..7ac9eb9a --- /dev/null +++ b/display/type340.c @@ -0,0 +1,706 @@ +/* + * $Id: type340.c,v 1.5 2004/01/24 20:52:16 phil Exp $ + * Simulator Independent DEC Type 340 Graphic Display Processor Simulation + * Phil Budne + * September 20, 2003 + * from vt11.c + * + * Information from DECUS 7-13 + * http://www.spies.com/~aek/pdf/dec/pdp7/7-13_340displayProgMan.pdf + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne + * + * 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 + * THE AUTHORS 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 the author shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization + * from the authors. + */ + +#include "display.h" /* XY plot interface */ + +/* + * The Type 340 was used on the PDP-{4,6,7,9,10} + * and used 18-bit words, with bits numbered 0 thru 17 + * (most significant to least) + */ + +#define BITMASK(N) (1<<(17-(N))) + +/* mask for a field */ +#define FIELDMASK(START,END) ((1<<((END)-(START)+1))-1) + +/* extract a field */ +#define GETFIELD(W,START,END) (((W)>>(17-(END)))&FIELDMASK(START,END)) + +/* extract a 1-bit field */ +#define TESTBIT(W,B) (((W) & BITMASK(B)) != 0) + +#ifdef DEBUG_TY340 +#define DEBUGF(X) printf X +#else +#define DEBUGF(X) +#endif + +typedef long ty340word; + +static ty340word DAC; /* Display Address Counter */ +static unsigned char shift; /* 1 bit */ +static enum mode mode; /* 3 bits */ +static int scale; /* 2 bits */ + +enum mode { PARAM=0, POINT, SLAVE, CHAR, VECTOR, VCONT, INCR, SUBR }; + +enum jump_type { DJP=2, DJS=3, DDS=1 }; +static ty340word ASR; /* Address Save Register */ +static unsigned char save_ff; /* "save" flip-flop */ + +static unsigned char intensity; /* 3 bits */ +static unsigned char lp_ena; /* 1 bit */ + +/* kept signed for raster violation checking */ +static short xpos, ypos; /* 10 bits, signed */ +static unsigned char sequence; /* 2 bits */ + +/* XXX make defines public for 340_cycle return */ +#define STOPPED 01 +#define LPHIT 02 +#define VEDGE 04 +#define HEDGE 010 +static unsigned char status = STOPPED; + +/* + * callbacks into PDP-6/10 simulator + */ +extern ty340word ty340_fetch(ty340word); +extern void ty340_store(ty340word, ty340word); +extern void ty340_stop_int(void); +extern void ty340_lp_int(void); + +void +ty340_set_dac(ty340word addr) +{ + DAC = addr; + mode = 0; + DEBUGF(("set DAC %06\r\n", DAC)); + status = 0; /* XXX just clear stopped? */ + /* XXX clear other stuff? save_ff? */ +} + +void +ty340_reset(void) +{ + /* XXX call display layer? destroy window? */ + xpos = ypos = 0; + status = STOPPED; +} + +static int +point(int x, int y, int seq) +{ + int i; + + /* XXX apply scale? */ + + i = DISPLAY_INT_MAX-7+intensity; + if (i <= 0) + i = 1; + + if (x < 0 || x > 1023) { + status |= VEDGE; + return 0; + } + if (y < 0 || y > 1023) { + status |= HEDGE; + return 0; + } + + if (display_point(x, y, i, 0)) { + if (lp_ena) { + /* XXX save location? */ + status |= LPHIT; + sequence = seq; + } + } +} + +/* + * two-step algorithm, developed by Xiaolin Wu + * from http://graphics.lcs.mit.edu/~mcmillan/comp136/Lecture6/Lines.html + */ + +/* + * The two-step algorithm takes the interesting approach of treating + * line drawing as a automaton, or finite state machine. If one looks + * at the possible configurations for the next two pixels of a line, + * it is easy to see that only a finite set of possibilities exist. + * The two-step algorithm shown here also exploits the symmetry of + * line-drawing by simultaneously drawn from both ends towards the + * midpoint. + */ + +static void +lineTwoStep(int x0, int y0, int x1, int y1) +{ + int dy = y1 - y0; + int dx = x1 - x0; + int stepx, stepy; + + if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } + if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } + + lpoint(x0,y0); + if (dx == 0 && dy == 0) /* following algorithm won't work */ + return; /* just the one dot */ + lpoint(x1, y1); + if (dx > dy) { + int length = (dx - 1) >> 2; + int extras = (dx - 1) & 3; + int incr2 = (dy << 2) - (dx << 1); + if (incr2 < 0) { + int c = dy << 1; + int incr1 = c << 1; + int d = incr1 - dx; + int i; + + for (i = 0; i < length; i++) { + x0 += stepx; + x1 -= stepx; + if (d < 0) { /* Pattern: */ + lpoint(x0, y0); + lpoint(x0 += stepx, y0); /* x o o */ + lpoint(x1, y1); + lpoint(x1 -= stepx, y1); + d += incr1; + } + else { + if (d < c) { /* Pattern: */ + lpoint(x0, y0); /* o */ + lpoint(x0 += stepx, y0 += stepy); /* x o */ + lpoint(x1, y1); + lpoint(x1 -= stepx, y1 -= stepy); + } else { + lpoint(x0, y0 += stepy); /* Pattern: */ + lpoint(x0 += stepx, y0); /* o o */ + lpoint(x1, y1 -= stepy); /* x */ + lpoint(x1 -= stepx, y1); + } + d += incr2; + } + } + if (extras > 0) { + if (d < 0) { + lpoint(x0 += stepx, y0); + if (extras > 1) lpoint(x0 += stepx, y0); + if (extras > 2) lpoint(x1 -= stepx, y1); + } else + if (d < c) { + lpoint(x0 += stepx, y0); + if (extras > 1) lpoint(x0 += stepx, y0 += stepy); + if (extras > 2) lpoint(x1 -= stepx, y1); + } else { + lpoint(x0 += stepx, y0 += stepy); + if (extras > 1) lpoint(x0 += stepx, y0); + if (extras > 2) lpoint(x1 -= stepx, y1 -= stepy); + } + } + } else { + int c = (dy - dx) << 1; + int incr1 = c << 1; + int d = incr1 + dx; + int i; + for (i = 0; i < length; i++) { + x0 += stepx; + x1 -= stepx; + if (d > 0) { + lpoint(x0, y0 += stepy); /* Pattern: */ + lpoint(x0 += stepx, y0 += stepy); /* o */ + lpoint(x1, y1 -= stepy); /* o */ + lpoint(x1 -= stepx, y1 -= stepy); /* x */ + d += incr1; + } else { + if (d < c) { + lpoint(x0, y0); /* Pattern: */ + lpoint(x0 += stepx, y0 += stepy); /* o */ + lpoint(x1, y1); /* x o */ + lpoint(x1 -= stepx, y1 -= stepy); + } else { + lpoint(x0, y0 += stepy); /* Pattern: */ + lpoint(x0 += stepx, y0); /* o o */ + lpoint(x1, y1 -= stepy); /* x */ + lpoint(x1 -= stepx, y1); + } + d += incr2; + } + } + if (extras > 0) { + if (d > 0) { + lpoint(x0 += stepx, y0 += stepy); + if (extras > 1) lpoint(x0 += stepx, y0 += stepy); + if (extras > 2) lpoint(x1 -= stepx, y1 -= stepy); + } else if (d < c) { + lpoint(x0 += stepx, y0); + if (extras > 1) lpoint(x0 += stepx, y0 += stepy); + if (extras > 2) lpoint(x1 -= stepx, y1); + } else { + lpoint(x0 += stepx, y0 += stepy); + if (extras > 1) lpoint(x0 += stepx, y0); + if (extras > 2) { + if (d > c) + lpoint(x1 -= stepx, y1 -= stepy); + else + lpoint(x1 -= stepx, y1); + } + } + } + } + } else { + int length = (dy - 1) >> 2; + int extras = (dy - 1) & 3; + int incr2 = (dx << 2) - (dy << 1); + if (incr2 < 0) { + int c = dx << 1; + int incr1 = c << 1; + int d = incr1 - dy; + int i; + for (i = 0; i < length; i++) { + y0 += stepy; + y1 -= stepy; + if (d < 0) { + lpoint(x0, y0); + lpoint(x0, y0 += stepy); + lpoint(x1, y1); + lpoint(x1, y1 -= stepy); + d += incr1; + } else { + if (d < c) { + lpoint(x0, y0); + lpoint(x0 += stepx, y0 += stepy); + lpoint(x1, y1); + lpoint(x1 -= stepx, y1 -= stepy); + } else { + lpoint(x0 += stepx, y0); + lpoint(x0, y0 += stepy); + lpoint(x1 -= stepx, y1); + lpoint(x1, y1 -= stepy); + } + d += incr2; + } + } + if (extras > 0) { + if (d < 0) { + lpoint(x0, y0 += stepy); + if (extras > 1) lpoint(x0, y0 += stepy); + if (extras > 2) lpoint(x1, y1 -= stepy); + } else + if (d < c) { + lpoint(x0, y0 += stepy); + if (extras > 1) lpoint(x0 += stepx, y0 += stepy); + if (extras > 2) lpoint(x1, y1 -= stepy); + } else { + lpoint(x0 += stepx, y0 += stepy); + if (extras > 1) lpoint(x0, y0 += stepy); + if (extras > 2) lpoint(x1 -= stepx, y1 -= stepy); + } + } + } else { + int c = (dx - dy) << 1; + int incr1 = c << 1; + int d = incr1 + dy; + int i; + for (i = 0; i < length; i++) { + y0 += stepy; + y1 -= stepy; + if (d > 0) { + lpoint(x0 += stepx, y0); + lpoint(x0 += stepx, y0 += stepy); + lpoint(x1 -= stepy, y1); + lpoint(x1 -= stepx, y1 -= stepy); + d += incr1; + } else { + if (d < c) { + lpoint(x0, y0); + lpoint(x0 += stepx, y0 += stepy); + lpoint(x1, y1); + lpoint(x1 -= stepx, y1 -= stepy); + } else { + lpoint(x0 += stepx, y0); + lpoint(x0, y0 += stepy); + lpoint(x1 -= stepx, y1); + lpoint(x1, y1 -= stepy); + } + d += incr2; + } + } + if (extras > 0) { + if (d > 0) { + lpoint(x0 += stepx, y0 += stepy); + if (extras > 1) lpoint(x0 += stepx, y0 += stepy); + if (extras > 2) lpoint(x1 -= stepx, y1 -= stepy); + } else if (d < c) { + lpoint(x0, y0 += stepy); + if (extras > 1) lpoint(x0 += stepx, y0 += stepy); + if (extras > 2) lpoint(x1, y1 -= stepy); + } else { + lpoint(x0 += stepx, y0 += stepy); + if (extras > 1) lpoint(x0, y0 += stepy); + if (extras > 2) { + if (d > c) + lpoint(x1 -= stepx, y1 -= stepy); + else + lpoint(x1, y1 -= stepy); + } + } + } + } + } +} /* lineTwoStep */ + +static int +vector(int i, int sx, int dx, int sy, int dy) +{ + int x0, y0, x1, y1; + + x0 = xpos; + y0 = ypos; + + if (sx) { + x1 = x0 - dx; + if (x1 < 0) /* XXX TEMP? */ + x1 = 0; + } + else { + x1 = x0 + dx; + if (x1 > 1023) /* XXX TEMP? */ + x1 = 1023; + } + + if (sy) { + y1 = y0 - dy; + if (y1 < 0) /* XXX TEMP? */ + y1 = 0; + } + else { + y1 = y0 + dy; /* XXX TEMP? */ + if (y1 > 1023) + y1 = 1023; + } + + DEBUGF(("vector i%d (%d,%d) to (%d,%d)\r\n", i, x0, y0, x1, y1)); + if (i) + lineTwoStep(x0, y0, x1, y1); + + xpos = x1; + ypos = y1; + return 0; +} + +/* return true on raster violation */ +int +ipoint(int i, int n, unsigned char byte) +{ + if (byte & 010) { /* left/right */ + if (byte & 04) { + if (xpos == 0) { + status |= VEDGE; + return 1; + } + xpos--; + } + else { + if (xpos == 1023) { + status |= VEDGE; + return 1; + } + xpos++; + } + } + if (byte & 02) { /* up/down */ + if (byte & 04) { + if (ypos == 0) { + status |= HEDGE; + return 1; + } + ypos--; + } + else { + if (ypos == 1023) { + status |= HEDGE; + return 1; + } + ypos++; + } + } + if (i) + point(xpos, ypos, n); + + return 0; +} + +/* + * 342 character generator - first 64 characters (from manual) + */ +static const unsigned char chars[64][5] = { + { 0070, 0124, 0154, 0124, 0070 }, /* 00 */ + { 0174, 0240, 0240, 0240, 0174 }, /* 01 A */ + { 0376, 0222, 0222, 0222, 0154 }, /* 02 B */ + { 0174, 0202, 0202, 0202, 0104 }, /* 03 C */ + { 0376, 0202, 0202, 0202, 0174 }, /* 04 D */ + { 0376, 0222, 0222, 0222, 0222 }, /* 05 E */ + { 0376, 0220, 0220, 0220, 0220 }, /* 06 F */ + { 0174, 0202, 0222, 0222, 0134 }, /* 07 G */ + { 0376, 0020, 0020, 0020, 0376 }, /* 10 H */ + { 0000, 0202, 0376, 0202, 0000 }, /* 11 I */ + { 0004, 0002, 0002, 0002, 0374 }, /* 12 J */ + { 0376, 0020, 0050, 0104, 0202 }, /* 13 K */ + { 0376, 0002, 0002, 0002, 0002 }, /* 14 K */ + { 0374, 0100, 0040, 0100, 0374 }, /* 15 M */ + { 0376, 0100, 0040, 0020, 0376 }, /* 16 N */ + { 0174, 0202, 0202, 0202, 0174 }, /* 17 O */ + { 0376, 0220, 0220, 0220, 0140 }, /* 20 P */ + { 0174, 0202, 0212, 0206, 0176 }, /* 21 Q */ + { 0376, 0220, 0230, 0224, 0142 }, /* 22 R */ + { 0144, 0222, 0222, 0222, 0114 }, /* 23 S */ + { 0200, 0200, 0376, 0200, 0200 }, /* 24 T */ + { 0374, 0002, 0002, 0002, 0374 }, /* 25 U */ + { 0370, 0004, 0002, 0004, 0370 }, /* 26 V */ + { 0376, 0004, 0010, 0004, 0376 }, /* 27 W */ + { 0202, 0104, 0070, 0104, 0202 }, /* 30 X */ + { 0200, 0100, 0076, 0100, 0200 }, /* 31 Y */ + { 0226, 0232, 0222, 0262, 0322 }, /* 32 Z */ + { 0000, 0000, 0000, 0000, 0000 }, /* 33 LF */ + { 0000, 0000, 0000, 0000, 0000 }, /* 34 CR */ + { 0000, 0000, 0000, 0000, 0000 }, /* 35 HORIZ */ + { 0000, 0000, 0000, 0000, 0000 }, /* 36 VERT */ + { 0000, 0000, 0000, 0000, 0000 }, /* 37 ESC */ + { 0000, 0000, 0000, 0000, 0000 }, /* 40 space */ + { 0000, 0000, 0372, 0000, 0000 }, /* 41 ! */ + { 0000, 0340, 0000, 0340, 0000 }, /* 42 " */ + { 0050, 0376, 0050, 0376, 0050 }, /* 43 # */ + { 0144, 0222, 0376, 0222, 0114 }, /* 44 $ */ + { 0306, 0310, 0220, 0246, 0306 }, /* 45 % */ + { 0154, 0222, 0156, 0004, 0012 }, /* 46 & */ + { 0000, 0000, 0300, 0340, 0000 }, /* 47 ' */ + { 0070, 0104, 0202, 0000, 0000 }, /* 50 ( */ + { 0000, 0000, 0202, 0104, 0070 }, /* 51 ) */ + { 0124, 0070, 0174, 0070, 0124 }, /* 52 * */ + { 0020, 0020, 0174, 0020, 0020 }, /* 53 + */ + { 0000, 0014, 0016, 0000, 0000 }, /* 54 , */ + { 0020, 0020, 0020, 0020, 0020 }, /* 55 - */ + { 0000, 0006, 0006, 0000, 0000 }, /* 56 . */ + { 0004, 0010, 0020, 0040, 0100 }, /* 57 / */ + { 0174, 0212, 0222, 0242, 0174 }, /* 60 0 */ + { 0000, 0102, 0376, 0002, 0000 }, /* 61 1 */ + { 0116, 0222, 0222, 0222, 0142 }, /* 62 2 */ + { 0104, 0202, 0222, 0222, 0154 }, /* 63 3 */ + { 0020, 0060, 0120, 0376, 0020 }, /* 64 4 */ + { 0344, 0222, 0222, 0222, 0214 }, /* 65 5 */ + { 0174, 0222, 0222, 0222, 0114 }, /* 66 6 */ + { 0306, 0210, 0220, 0240, 0300 }, /* 67 7 */ + { 0154, 0222, 0222, 0222, 0154 }, /* 70 8 */ + { 0144, 0222, 0222, 0222, 0174 }, /* 71 9 */ + { 0000, 0066, 0066, 0000, 0000 }, /* 72 : */ + { 0000, 0154, 0156, 0000, 0000 }, /* 73 ; */ + { 0020, 0050, 0104, 0202, 0000 }, /* 74 < */ + { 0050, 0050, 0050, 0050, 0050 }, /* 75 = */ + { 0000, 0202, 0104, 0050, 0020 }, /* 76 > */ + { 0100, 0200, 0236, 0220, 0140 } /* 77 ? */ +}; + +/* + * type 342 Character/Symbol generator for type 340 display + * return true if ESCaped + */ +static int +character(int n, char c) +{ + int x, y; + + switch (c) { + case 033: /* LF */ + if (ypos < 12) { + status |= HEDGE; + ypos = 0; + } + else + ypos -= 12; /* XXX scale? */ + + return 0; + case 034: /* CR */ + xpos = 0; + return 0; + case 035: /* shift in */ + shift = 1; + return 0; + case 036: /* shift out */ + shift = 0; + return 0; + case 037: /* escape */ + sequence = n; + return 1; + } + /* XXX plot character from character set selected by "shift" + * (offset index by 64?) + */ + for (x = 0; x < 5; x++) { + for (y = 0; y < 7; y++) { + if (chars[c][x] & (1< 1023) { + xpos = 1023; + status |= VEDGE; + } + return 0; +} + +int +ty340_cycle(int us, int slowdown) +{ + ty340word inst, addr; + int i, escape, stopped; + + if (status & STOPPED) + return 0; /* XXX age display? */ + + inst = ty340_fetch(DAC); + DEBUGF(("%06o: %06o\r\n", DAC, inst)); + DAC++; + + escape = 0; + switch (mode) { + case PARAM: + mode = GETFIELD(inst, 2, 4); + + if (TESTBIT(inst, 5)) { /* load l.p. enable */ + lp_ena = TESTBIT(inst,6); + DEBUGF(("lp_ena %d\r\n", lp_ena)); + } + + if (TESTBIT(inst, 7)) { + status |= STOPPED; + if (TESTBIT(inst, 8)) + ty340_stop_int(); /* set stop_int_end? */ + } + + if (TESTBIT(inst, 11)) + scale = GETFIELD(inst, 12, 13); + + if (TESTBIT(inst, 14)) + intensity = GETFIELD(inst, 15, 17); + + break; + + case POINT: + mode = GETFIELD(inst, 2, 4); + + if (TESTBIT(inst, 5)) /* load l.p. enable */ + lp_ena = TESTBIT(inst,6); + + if (TESTBIT(inst, 1)) + ypos = GETFIELD(inst, 8, 17); + else + xpos = GETFIELD(inst, 8, 17); + + if (TESTBIT(inst, 7)) + point(xpos, ypos, 0); + break; + + case SLAVE: + mode = GETFIELD(inst, 2, 4); + break; + + case CHAR: + escape = (character(0, GETFIELD(inst, 0, 5)) || + character(1, GETFIELD(inst, 6, 11)) || + character(2, GETFIELD(inst, 12, 17))); + break; + + case VECTOR: + escape = TESTBIT(inst, 0); + if (vector(TESTBIT(inst, 1), + TESTBIT(inst, 2), GETFIELD(inst, 3, 9), + TESTBIT(inst, 10), GETFIELD(inst, 11, 17))) { + /* XXX interrupt? */ + } + break; + case VCONT: + escape = TESTBIT(inst, 0); + if (vector(TESTBIT(inst, 1), + TESTBIT(inst, 2), GETFIELD(inst, 3, 9), + TESTBIT(inst, 10), GETFIELD(inst, 11, 17))) { + /* XXX set escape? */ + mode = PARAM; /* raster violation */ + } + break; + + case INCR: + escape = TESTBIT(inst, 0); /* escape bit */ + i = TESTBIT(inst, 1); + + if (ipoint(i, 0, GETFIELD(inst, 2, 5)) || + ipoint(i, 1, GETFIELD(inst, 6, 9)) || + ipoint(i, 2, GETFIELD(inst, 10, 13)) || + ipoint(i, 3, GETFIELD(inst, 14, 17))) + /* XXX set escape? */ + mode = PARAM; /* raster violation */ + break; + + case SUBR: + /* type 347 Display Subroutine Option? */ + + mode = GETFIELD(inst, 2, 4); + /* XXX take high bits of current DAC? */ + addr = GETFIELD(inst, 5, 17); + + switch (GETFIELD(inst, 0, 1)) { + case DJS: /* display jump and save */ + ASR = DAC; + save_ff = 1; /* set "save" flip-flop */ + /* FALL */ + case DJP: /* display jump */ + DAC = addr; + break; + case DDS: /* display deposit save register */ + ty340_deposit(addr, (DJP<<16) | ASR); + save_ff = 0; /* ?? */ + break; + default: + /* XXX ??? */ + break; + } + break; + } + + if (escape) { + mode = PARAM; + if (save_ff) { + /* return from subroutine */ + DAC = ASR; + save_ff = 0; + } + } + return status; +} /* ty340_cycle */ diff --git a/display/vt11.c b/display/vt11.c new file mode 100644 index 00000000..7dddadb0 --- /dev/null +++ b/display/vt11.c @@ -0,0 +1,3148 @@ +/* + * $Id: vt11.c,v 1.19 2004/02/07 06:38:12 phil Exp $ + * Simulator Independent VT11/VS60 Graphic Display Processor Simulation + * Started by Phil Budne September 13, 2003 + * Substantially revised by Douglas A. Gwyn, 05 Feb. 2004 + * + * from EK-VT11-TM-001, September 1974 + * and EK-VT48-TM-001, November 1976 + * with help from Al Kossow's "VT11 instruction set" posting of 21 Feb 93 + * and VT48 Engineering Specification Rev B + * and VS60 diagnostic test listings, provided by Alan Frisbie + * + * The VT11 is a calligraphic display-file device used in the GT4x series + * of workstations (PDP-11/04,34,40 based). + * + * The VS60 is an improved, extended, upward-compatible version of the + * VT11, used in the GT62 workstation (PDP-11/34 based). It supported + * dual consoles (CRTs with lightpens), multiple phosphor colors, 3D + * depth cueing, and circle/arc generator as options. We do not know + * whether any of these options were ever implemented or delivered. + * XXX VS60 depth-cueing option not yet fully implemented + * + * The VSV11/VS11 is a color raster display-file device (with joystick + * instead of light pen) with instructions similar to the VT11's but + * different enough that a separate emulation should be created by + * editing a copy of this source file rather than trying to hack it into + * this one. Very likely, the display (phosphor decay) simulation will + * also require revision to handle multiple colors. + * + * A PDP-11 system has at most one display controller attached. + * In principle, a VT11 or VS60 can also be used on a VAX Unibus. + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne and Douglas A. Gwyn + * + * 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 + * THE AUTHORS 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 the authors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization + * from the authors. + */ + +#ifdef DEBUG_VT11 +#include +#endif +#include /* memset */ +#ifndef NO_CONIC_OPT +#include /* atan2, cos, sin, sqrt */ +#endif + +#include "display.h" /* XY plot interface */ +#include "vt11.h" + +#define BITMASK(n) (1<<(n)) /* PDP-11 bit numbering */ + +/* mask for a field */ +#define FIELDMASK(START,END) ((1<<((START)-(END)+1))-1) + +/* extract a field */ +#define GETFIELD(W,START,END) (((W)>>(END)) & FIELDMASK(START,END)) + +/* extract a 1-bit field */ +#define TESTBIT(W,B) (((W) & BITMASK(B)) != 0) + +#ifdef DEBUG_VT11 +#define DEBUGF(X) do { printf X; fflush(stdout); } while (0) +#else +#define DEBUGF(X) +#endif + +/* + * Note about coordinate signedness and wrapping: + * + * There is a discrepancy between the documentation and the known or suspected + * behavior of these devices. The VT11 manual says it wraps 12-bit position + * coordinates independently of sign (4097 -> 1), but then it doesn't mention + * any sign for X,Y position registers, yet we think that graphics is properly + * tracked in the negative domain. The VS60 manual says it wraps from 4097 to + * -4095, but there is no evidence of this in the manual's diagrams, and the + * absolute-point graphic data format doesn't show signs for X,Y coordinates. + * Most likely, both devices have signed position coordinates (either sign- + * magnitude or twos-complement) and merely drop bits that overflow. + * + * At one point this simulation attempted to implement position wrapping, but + * because it adds overhead, has uncertain characteristics, and should never + * be exploited by any application anyway, now no wrapping is done, and the + * position is tracked using at least 32 bits including sign. + * + * Note about scaling and offsets: + * + * The VS60 supports character and vector scaling and position offsets. The + * X, Y, and Z position register values always include scaling and offsets. + * It is not clear from the manual whether or not there are two "guard bits", + * which would better track the virtual position when using a scale factor of + * 3/4, 1/2, or 1/4. Most likely, there are no guard bits. This simulation + * maintains position values and offsets both multiplied by PSCALEF, which + * should be 4 to obtain maximum drawing precision, or 1 to mimic non-guard-bit + * display hardware. These internal coordinates are "normalized" (converted to + * correct virtual CRT coordinates) before being reported via the position/ + * offset registers. The normalized Z position register value's 2 lowest bits + * are always 0. + * Example of why this matters: Set vector scale 1/2; draw successive vectors + * with delta X = 1, 1, and -2. With guard bits, the final and original X + * positions are the same; without guard bits, the final X position is one + * unit to the left of the original position. This effect accumulates over a + * long sequence of vectors, leading to quite visible distortion of the image. + * + * Light-pen and edge-interrupt positions always have "on-screen" values. + */ + +#ifndef PSCALEF +#if 1 /* XXX temporary during development, to catch any oversights */ +#define PSCALEF 4 /* position scale factor 4 for maximum precision */ +#else +#define PSCALEF 1 /* position scale factor 1 for accurate simulation */ +#endif +#endif + +#define PSCALE(x) ((x) * PSCALEF) +#define PNORM(x) ((x) / PSCALEF) +/* virtual_CRT_coordinate = PNORM(scaled_value) */ + +/* VS60 scales points/vectors and characters separately */ +#define VSCALE(x) (PSCALE(vector_scale * (int32)(x)) / 4) +#define CSCALE(x) (PSCALE(char_scale * (int32)(x)) / 4) + +#define ABS(x) ((x) >= 0 ? (x) : -(x)) + +enum display_type vt11_display = DISPLAY_TYPE; /* DIS_VR{14,17,48} */ +int vt11_scale = PIX_SCALE; /* RES_{FULL,HALF,QUARTER,EIGHTH} */ +unsigned char vt11_init = 0; /* set after display_init() called */ +#define INIT { if (!vt11_init) { display_init(vt11_display, vt11_scale); \ + vt11_init = 1; vt11_reset(); } } + +/* state visible to host */ + +/* The register and field names are those used in the VS60 manual (minus the + trailing "flag", "code", "status", or "select"); the VT11 manual uses + somewhat different names. */ + +/* + * Display Program Counter + * Read/Write (reading returns the *relocated* DPC bits [15:0]) + * DPC address 15:1 + * resume 0 + */ +#define DPC sp->_dpc /* Display PC (always even) */ +static uint16 bdb = 0; /* Buffered Data Bits register; + see comment in vt11_get_dpc() */ + +/* + * Mode Parameter Register + * Read Only, except that writing to it beeps the LK40 keyboard's bell + * internal stop flag 15 + * graphic mode code 14:11 + * intensity level 10:8 + * LP con. 0 hit flag 7 + * shift out status 6 + * edge indicator 5 + * italics status 4 + * blink status 3 + * edge flag status 2 (VS60 only) + * line type register status 1:0 + */ +static unsigned char internal_stop = 0; /* 1 bit: stop display */ + +#define graphic_mode sp->_mode /* 4 bits: sets type for graphic data */ +enum mode { CHAR=0, SVECTOR, LVECTOR, POINT, GRAPHX, GRAPHY, RELPOINT, /* all */ + BSVECT, CIRCLE, ABSVECTOR /* VS60 only */ +}; + +#define intensity sp->_intens /* 3 bits: 0 => dim .. 7 => bright */ +static unsigned char lp0_flag = 0; /* 1 bit: light pen #0 detected hit */ +#define shift_out sp->_so /* 1 bit: chars using shift-out codes */ +static unsigned char edge_indic = 0; /* 1 bit: end pt outside visible area; + on-to-off transition only! */ +#define italics sp->_italics /* 1 bit: use italic font */ +#define blink_ena sp->_blink /* 1 bit: blink graphic item */ +static unsigned char edge_flag = 0; /* 1 bit: any edge transition (VS60) */ +#define line_type sp->_ltype /* 2 bits: style for drawing vectors */ +enum linetype { SOLID=0, LONG_DASH, SHORT_DASH, DOT_DASH }; + +/* + * Graphplot Increment and X Position Register + * Read Only + * graphplot increment register value 15:10 + * X position register value 9:0 + */ +static unsigned char graphplot_step = 0;/* (scaled) graphplot step increment */ +static int32 xpos = 0; /* X position register * PSCALEF */ + /* note: offset has been applied! */ +static int lp_xpos; /* (normalized) */ +static int edge_xpos; /* (normalized) */ + +/* + * Character Code and Y Position Register + * Read Only + * character register contents 15:10 + * Y position register value 9:0 + */ +static unsigned char char_buf = 0; /* (only lowest 6 bits reported) */ +static int32 ypos = 0; /* Y position register * PSCALEF */ + /* note: offset has been applied! */ +static int lp_ypos; /* (normalized) */ +static int edge_ypos; /* (normalized) */ + +/* + * Relocate Register (VS60 only) + * Read/Write + * spare 15:12 + * relocate register value[17:6] 11:0 + */ +static uint32 reloc = 0; /* relocation, aligned with DPC */ + +/* + * Status Parameter Register (VS60 only) + * Read Only, except for bit 7 (1 => external stop request) + * display busy status 15 + * stack overflow status 13 + * stack underflow status 12 + * time out status 11 + * char. rotate status 10 + * char. scale index 9:8 + * external stop flag 7 + * menu status 6 + * relocated DPC bits [17:16] 5:4 + * vector scale 3:0 + */ +#define busy (!(internal_stop || ext_stop || lphit_irq || lpsw_irq || edge_irq \ + || char_irq || stack_over || stack_under || time_out || name_irq)) + /* 1 bit: display initiated | resumed */ +static unsigned char stack_over = 0; /* 1 bit: "push" with full stack */ +static unsigned char stack_under = 0; /* 1 bit: "pop" with empty stack */ +static unsigned char time_out = 0; /* 1 bit: timeout has occurred */ +#define char_rotate sp->_crotate /* 1 bit: rotate chars 90 degrees CCW */ +#define cs_index sp->_csi /* character scale index 0..3 */ +static unsigned char ext_stop = 0; /* 1 bit: stop display */ +#define menu sp->_menu /* 1 bit: VS60 graphics in menu area */ +#define vector_scale sp->_vscale /* non-character scale factor * 4 */ + +/* + * X Offset Register (VS60 only) + * Read/Write + * upper X position bits 15:12 (read) + * sign of X dynamic offset 13 (write) + * X dynamic offset 11:0 + */ +static unsigned char s_xoff = 0; /* sign bit for xoff (needed for -0) */ +static int32 xoff = 0; /* X offset register * PSCALEF */ + +/* + * Y Offset Register (VS60 only) + * Read/Write + * upper Y position bits 15:12 (read) + * sign of Y dynamic offset 13 (write) + * Y dynamic offset 11:0 + */ +static unsigned char s_yoff = 0; /* sign bit for yoff (needed for -0) */ +static int32 yoff = 0; /* Y offset register * PSCALEF */ + +/* + * Associative Name Register (VS60 only) + * Write Only + * search code change enable 14 + * search code 13:12 + * name change enable 11 + * associative name 10:0 + */ +static unsigned char search = 0; /* 00=> no search, no interrupt + 01 => intr. on 11-bit compare + 10 => intr. on high-8-bit compare + 11 => intr. on high-4-bit compare */ +static unsigned assoc_name = 0; /* compare value */ + +/* + * Slave Console/Color Register (VS60 only) + * Read/Write * + * inten enable con. 0 15 + * light pen hit flag con. 0 14 * + * LP switch on flag con. 0 13 * + * LP switch off flag con. 0 12 * + * LP flag intr. enable con. 0 11 + * LP switch flag intr. enable con. 0 10 + * inten enable con. 1 9 + * light pen hit flag con. 1 8 * + * LP switch on flag con. 1 7 * + * LP switch off flag con. 1 6 * + * LP flag intr. enable con. 1 5 + * LP switch flag intr. enable con. 1 4 + * color 3:2 + * + * * indicates that maintenance switch 3 must be set to write these bits; + * the other bits are not writable at all + */ +#define int0_scope sp->_inten0 /* enable con. 0 for all graphic data */ +/* lp0_flag has already been defined, under Mode Parameter Register */ +#define lp0_sw display_lp_sw /* (defined in display.c) */ +#define lp0_intr_ena sp->_lp0intr /* generate interrupt on LP #0 hit */ +#define lp0_sw_intr_ena sp->_lp0swintr /* generate intr. on LP #0 switch chg */ +#define int1_scope sp->_inten1 /* enable con. 1 for all graphic data */ +/* following 2 flags only mutable via writing this register w/ MS3 set: */ +static unsigned char lp1_flag = 0; /* 1 bit: light pen #1 detected hit */ +static unsigned char lp1_sw = 0; /* 1 bit: LP #1 switch changed state */ +#define lp1_intr_ena sp->_lp1intr /* generate interrupt on LP #1 hit */ +#define lp1_sw_intr_ena sp->_lp1swintr /* generate intr. on LP #1 switch chg */ + +enum scolor { GREEN=0, YELLOW, ORANGE, RED }; +#define color sp->_color /* 2 bits: VS60 color option */ + +/* + * Name Register (VS60 only) + * Read Only + * name/assoc name match flag 15 + * search code 13:12 + * name 10:0 + */ +static unsigned char name_flag = 0; /* 1 bit: name matches associative nm */ +/* search previously defined, under Associative Name Register */ +#define name sp->_name /* current name from display file */ + +/* + * Stack Data Register (VS60 only) + * Read Only + * stack data 15:0 (as selected by Stk. Addr./Maint. Reg.) + * + * On the actual hardware there are 2 32-bit words per each of 8 stack levels. + * At the PDP-11 these appear to be 4 16-bit words ("stack bytes") per level. + */ +/* Two alternatives: keep active data in globals or use directly from stack. + The former is presumably more efficient when the stack isn't being used, + as in VT11 mode, but on most host processors the difference is small. + POPR is faster if we do not have to restore data to all those globals. + There are thus 9 levels of data, the initial state and 8 stacked sets. + Mimicking the actual hardware, the stack level *decreases* upon JSR. + */ + +static struct frame + { + vt11word _dpc; /* Display Program Counter (even) */ + unsigned _name; /* (11-bit) name from display file */ + enum mode _mode; /* 4 bits: sets type for graphic data */ + unsigned char _vscale; /* non-character scale factor * 4 */ + unsigned char _csi; /* character scale index 0..3 */ + unsigned char _cscale; /* character scale factor * 4 */ + unsigned char _crotate; /* rotate chars 90 degrees CCW */ + unsigned char _intens; /* intensity: 0 => dim .. 7 => bright */ + enum linetype _ltype; /* line type (long dash, etc.) */ + unsigned char _blink; /* blink enable */ + unsigned char _italics; /* italicize characters */ + unsigned char _so; /* chars are using shift-out codes */ + unsigned char _menu; /* VS60 graphics in menu area */ + unsigned char _cesc; /* perform POPR on char. term. match */ + unsigned char _edgeintr; /* generate intr. on edge transition */ + unsigned char _lp1swintr; /* generate intr. on LP #1 switch chg */ + unsigned char _lp0swintr; /* generate intr. on LP #0 switch chg */ + unsigned char _lp1intr; /* generate interrupt on LP #1 hit */ + unsigned char _inten1; /* blank cons. 1 for all graphic data */ + unsigned char _lp0intr; /* generate interrupt on LP #0 hit */ + unsigned char _inten0; /* blank cons. 0 for all graphic data */ + unsigned char _bright; /* visually indicate hit on entity */ + unsigned char _stopintr; /* generate interrupt on stop */ + enum scolor _color; /* scope display color (option) */ + unsigned char _zdata; /* flag: display file has Z coords */ + unsigned char _depth; /* flag: display Z using depth cue */ + } stack[9] = { { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, CHAR, 4, 1, 0, 4, SOLID, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, + }; +/* (stack pointer "sp" defined under Stack Address/Maintenance Register) */ + +#define char_scale sp->_cscale /* character scale factor * 4 */ +static const unsigned char csi2csf[4] = { 2, 4, 6, 8 }; /* maps cs_index to " */ +#define char_escape sp->_cesc /* perform POPR on char. term. match */ +#define edge_intr_ena sp->_edgeintr /* generate intr. on edge transition */ +#define lp_intensify sp->_bright /* if VT11, 20us bright spot; + if VS60, "bright down" the entity */ +#define stop_intr_ena sp->_stopintr /* generate intr. on internal stop */ +#define file_z_data sp->_zdata /* flag: display file has Z coords */ +#define depth_cue_proc sp->_depth /* flag: display Z using depth cue */ + +/* + * Character String Terminate Register (VS60 only) + * Read/Write + * char. term. reg. enable 7 + * character terminate code 6:0 + */ +static int char_term = 0; /* char. processing POPRs after this */ + +/* + * Stack Address/Maintenance Register (VS60 only) + * Read/Write + * maint. sw. 4 15 + * maint. sw. 3 14 + * maint. sw. 2 13 + * maint. sw. 1 12 + * offset mode status 10 + * jump to subr. ?rel. status 9 (diagnostic requires this be JSR abs.!) + * word 2 status 8 + * word 1 status 7 + * word 0 status 6 + * stack reset status 5 + * stack level select 4:2 (manual has this messed up) + * stack halfword select 1:0 (manual has this messed up) + */ +static unsigned char maint4 = 0; /* 1 bit: maintenance switch #4 */ +static unsigned char maint3 = 0; /* 1 bit: maintenance switch #3 */ +static unsigned char maint2 = 0; /* 1 bit: maintenance switch #2 */ +static unsigned char maint1 = 0; /* 1 bit: maintenance switch #1 */ +static unsigned char offset = 0; /* 1 bit: last data loaded offsets */ +static unsigned char jsr = 0; /* 1 bit: last control was JSR ?rel. */ +static unsigned char word_number = 0; /* tracks multiple data words etc. */ +static struct frame *sp = &stack[8]; +#define STACK_EMPTY (sp == &stack[8]) +#define STACK_FULL (sp == stack) +static unsigned char stack_sel = 8<<2; /* 8 levels, 4 PDP-11 words per level */ + /* stack_sel must track sp! */ + +/* + * Z Position Register, Depth Cue Option (VS60 only) + * Read/Write + * Z position register value[13:2] 11:0 + */ +static int32 zpos = 0; /* (Z "position" reg. * 4) * PSCALEF */ + /* note: offset has been applied! */ +static int32 lp_zpos; /* (scaled) */ +static int32 edge_zpos; /* (scaled) */ + +/* + * Z Offset Register, Depth Cue Option (VS60 only) + * Read/Write + * sign of X dynamic offset 15 (read) (VT48 manual has this confused) + * sign of Y dynamic offset 14 (read) (VT48 manual has this confused) + * sign of Z dynamic offset 13 + * Z dynamic offset 11:0 + */ +static unsigned char s_zoff = 0; /* sign bit for zoff (needed for -0) */ +static int32 zoff = 0; /* Z offset register * PSCALEF */ + +/* + * Invisible state: + */ +static unsigned char char_irq = 0; /* intr. on illegal char in SO mode */ +static unsigned char lphit_irq = 0; /* intr. on light-pen hit */ +static unsigned char lpsw_irq = 0; /* intr. on tip-switch state change */ +static unsigned char edge_irq = 0; /* intr. on edge transition */ +static unsigned char name_irq = 0; /* intr. on name match */ + +static unsigned char lp0_sw_state = 0; /* track light-pen tip-switch state */ +static unsigned char blink_off = 0; /* set when blinking graphics is dark */ +static unsigned char finish_jmpa = 0; /* reminder to fetch JMPA address */ +static unsigned char finish_jsra = 0; /* reminder to fetch JSRA address */ + +static unsigned char more_vect = 0; /* remembers LP hit in middle of vec. */ +static unsigned char more_arc = 0; /* remembers LP hit in middle of arc */ +static int32 save_x0, save_y0, save_x1, save_y1;/* CRT coords for rest of vec */ + +static unsigned char lp_suppress = 0; /* edge columns of char. (VT11 only) */ +static unsigned char stroking = 0; /* set when drawing VS60 char strokes */ +static unsigned char skip_start = 0; /* set between vis. char./arc strokes */ + +static unsigned char sync_period = 0; /* frame sync period (msec) */ +static unsigned char refresh_rate = 0; /* 2 bits: + 00 => continuous display refresh + 01 => 30 fps (60 fps if VT11) + 10 => 40 fps (VS60) + 11 => external sync (VS60) */ + +#if 0 /* this is accurate in simulated "real" time */ +#define BLINK_COUNT 266 /* 266 milliseconds */ +#else /* this looks better in actual real time (adjust for your host speed) */ +#define BLINK_COUNT 67 /* 67 milliseconds */ +#endif + +unsigned char vt11_csp_w = VT11_CSP_W; /* horizontal character spacing */ +unsigned char vt11_csp_h = VT11_CSP_H; /* vertical character spacing */ + +/* VS60 has a menu area to the right of the "main working surface" */ +#define MENU_OFFSET (1024 + VR48_GUTTER) /* left edge of menu on CRT */ +#define VR48_WIDTH (MENU_OFFSET + 128) /* X beyond this is not illuminated */ + +static int reduce; /* CRT units per actual pixel */ +static int x_edge; /* 1023 or VR48_WIDTH-1, depending */ +static int y_edge; /* 767 or 1023, depending on display */ +#define ONCRT(x,y) ((x) >= 0 && (x) <= x_edge && (y) >= 0 && (y) <= y_edge) + +/* + * Uncertain whether VS60 edge transitions in menu area are flagged and whether + * clipping takes menu width into account. Three possibilities: + */ +#if 0 /* menu area never clipped (seems wrong) */ +#define ONSCREEN(x,y) (menu || ((x)>=0 && (x)<=1023 && (y)>=0 && (y)<=y_edge)) +#elif 0 /* menu area correctly clipped */ +#define ONSCREEN(x,y) ((x) >= 0 && (x) <= (menu ? 127 : 1023) \ + && (y) >= 0 && (y) <= y_edge) +#else /* menu area clipped same as main area */ +#define ONSCREEN(x,y) ((x) >= 0 && (x) <= 1023 && (y) >= 0 && (y) <= y_edge) + +#endif + +static void lineTwoStep(int32, int32, int32, int32); /* forward reference */ + +/* + * calls to read/write VT11/VS60 CSRs + * + * presumably the host looks at our state less often than we do(!) + * so we keep it in a form convenient to us! + */ + +int32 +vt11_get_dpc(void) +{ INIT + /* + * The VT48 manual says that Maintenance Switch 1 causes the Buffered + * Data Bits register to be "entered into the DPC" so it can be + * examined by reading the DPC address, but details of when and how + * often that happens are not provided. Examination of the diagnostic + * test listings show that relocation is applied and that only the DPC + * is involved when this switch is set. + */ + return ((maint1 ? bdb : DPC) + reloc) & 0177777; +} + +void +vt11_set_dpc(uint16 d) +{ INIT + bdb = d; /* save all bits in case maint1 used */ + DEBUGF(("set DPC 0%06o\r\n", (unsigned)d)); + if (!TESTBIT(d,0)) { + sp = &stack[8]; /* important! do this first */ + stack_sel = 8<<2; + DPC = d; /* load DPC */ + sync_period = 0; + ext_stop = 0; + /* the following seem reasonable, but might be wrong */ + finish_jmpa = finish_jsra = jsr = 0; + word_number = 0; +#if 0 /* probably accurate mimicry, but ugly behavior */ + if (edge_irq) { + xpos = PSCALE(edge_x); + ypos = PSCALE(edge_y); + } +#endif + } else { /* RESUME (after intr); DPC unchanged */ + /* if resuming from LP hit interrupt, finish drawing rest of vector */ + /* (if resuming from edge interrupt, vector is *not* drawn) */ + if (more_vect) { + unsigned char save_ena = lp0_intr_ena; + lp0_intr_ena = 0; /* one hit per vector is plenty */ + lphit_irq = 0; /* or else lineTwoStep aborts again! */ + /* line_counter is intact; draw rest of visible vector */ + lineTwoStep(save_x0, save_y0, save_x1, save_y1); + lp0_intr_ena = save_ena; + } + if (more_arc) { /* remainder of chord was just drawn */ + unsigned char save_ena = lp0_intr_ena; + lp0_intr_ena = 0; /* one hit per arc is plenty */ + lphit_irq = 0; /* or else lineTwoStep aborts again! */ + /* line_counter is intact; draw rest of visible arc */ + /*XXX not yet implemented [conic{23}() needed]*/ + lp0_intr_ena = save_ena; + } + if (!maint2) /* kludge to satify diagnostic test */ + ext_stop = 0; + } + internal_stop = time_out = stack_over = stack_under = 0; + more_vect = more_arc = stroking = skip_start = 0; + edge_indic = edge_flag = lp0_flag = lp1_flag = lp_suppress = 0; + char_irq = lphit_irq = lpsw_irq = edge_irq = name_irq = 0; + /* next vt11_cycle() will perform a fetch */ +} + +int32 +vt11_get_mpr(void) +{ + int32 ret; + INIT + ret = (internal_stop<<15) | (graphic_mode<<11) | (intensity<<8) | + (lp0_flag<<7) | (shift_out<<6) | (edge_indic<<5) | (italics<<4) | + (blink_ena<<3) | line_type; + + if (VS60) + ret |= edge_flag<<2; + + return ret; +} + +void +vt11_set_mpr(uint16 d) +{ INIT + /* beeps the "bell" on the LK40 keyboard */ +#if 0 /* probably doesn't hurt to do it for the VS60 also */ + if (VT11) /* according to the VS60 specs */ +#endif + display_beep(); +} + +int32 +vt11_get_xpr(void) +{ + int32 pos; + INIT + pos = lphit_irq ? lp_xpos : edge_irq ? edge_xpos : PNORM(xpos); + return (graphplot_step << 10) | GETFIELD(ABS(pos),9,0); +} + +void +vt11_set_xpr(uint16 d) +{ INIT + DEBUGF(("set XPR: no effect\r\n")); +} + +int32 +vt11_get_ypr(void) +{ + int32 pos; + INIT + pos = lphit_irq ? lp_ypos : edge_irq ? edge_ypos : PNORM(ypos); + return (GETFIELD(char_buf,5,0) << 10) | GETFIELD(ABS(pos),9,0); +} + +void +vt11_set_ypr(uint16 d) +{ INIT + DEBUGF(("set YPR: no effect\r\n")); +} + +/* All the remaining registers pertain to the VS60 only. */ + +int32 +vt11_get_rr(void) +{ INIT + return reloc >> 6; +} + +void +vt11_set_rr(uint16 d) +{ INIT + reloc = (uint32)GETFIELD(d,11,0) << 6; +} + +int32 +vt11_get_spr(void) +{ INIT + return (busy<<15) | (stack_over<<13) | (stack_under<<12) | (time_out<<11) | + (char_rotate<<10) | (cs_index<<8) | (ext_stop<<7) | + (menu<<6) | (((DPC + reloc) & 0600000L) >> 12) | vector_scale; +} + +void +vt11_set_spr(uint16 d) +{ INIT + ext_stop = TESTBIT(d,7); + + if (ext_stop /* && stop_intr_ena */)/* not maskable? */ + vt_stop_intr(); /* post stop interrupt to host */ + /* (asynchronous with display cycle) */ +} + +int32 +vt11_get_xor(void) +{ + int32 off, pos; + INIT + off = PNORM(xoff); + pos = lphit_irq ? lp_xpos : edge_irq ? edge_xpos : PNORM(xpos); + return (GETFIELD(ABS(pos),13,10)<<12) | GETFIELD(ABS(off),11,0); +} + +void +vt11_set_xor(uint16 d) +{ INIT + xoff = PSCALE(GETFIELD(d,11,0)); + if (s_xoff = TESTBIT(d,13)) + xoff = -xoff; +} + +int32 +vt11_get_yor(void) +{ + int32 off, pos; + INIT + off = PNORM(yoff); + pos = lphit_irq ? lp_ypos : edge_irq ? edge_ypos : PNORM(ypos); + return (GETFIELD(ABS(pos),13,10)<<12) | GETFIELD(ABS(off),11,0); +} + +void +vt11_set_yor(uint16 d) +{ INIT + yoff = PSCALE(GETFIELD(d,11,0)); + if (s_yoff = TESTBIT(d,13)) + yoff = -yoff; +} + +int32 +vt11_get_anr(void) +{ INIT + DEBUGF(("get ANR: no effect\r\n")); + return (search << 12) | assoc_name; /* [garbage] */ +} + +void +vt11_set_anr(uint16 d) +{ INIT + if (TESTBIT(d,14)) + search = GETFIELD(d,13,12); + if (TESTBIT(d,11)) + assoc_name = GETFIELD(d,10,0); +} + +int32 +vt11_get_scr(void) +{ INIT + return (int0_scope<<15) | (lp0_flag<<14) | (lp0_sw<<13) | ((!lp0_sw)<<12) | + (lp0_intr_ena<<11) | (lp0_sw_intr_ena<<10) | (int1_scope<<9) | + (lp1_flag<<8) | (lp1_sw<<7) | ((!lp1_sw)<<6) | (lp1_intr_ena<<5) | + (lp1_sw_intr_ena<<4) | (color << 2); +} + +void +vt11_set_scr(uint16 d) +{ INIT + if (maint3) { + unsigned char old_sw0 = lp0_sw; + unsigned char old_sw1 = lp1_sw; + + if (TESTBIT(d,14)) { + if (!lphit_irq) { /* ensure correct position registers reported */ + lp_xpos = PNORM(xpos); + lp_ypos = PNORM(ypos); + lp_zpos = PNORM(zpos); + } + lp0_flag = 1; + if (lp0_intr_ena) + lphit_irq = 1; + } + if (TESTBIT(d,13)) { + lp0_sw = 1; /* the manual seems to have it backward */ + if (lp0_sw_intr_ena && lp0_sw != old_sw0) + lpsw_irq = 1; + } + if (TESTBIT(d,12)) { + lp0_sw = 0; /* the manual seems to have it backward */ + if (lp0_sw_intr_ena && lp0_sw != old_sw0) + lpsw_irq = 1; + } + if (TESTBIT(d,8)) { + if (!lphit_irq) { /* ensure correct position registers reported */ + lp_xpos = PNORM(xpos); + lp_ypos = PNORM(ypos); + lp_zpos = PNORM(zpos); + } + lp1_flag = 1; + if (lp1_intr_ena) + lphit_irq = 1; + } + if (TESTBIT(d,7)) { + lp1_sw = 1; + if (lp1_sw_intr_ena && lp1_sw != old_sw1) + lpsw_irq = 1; + } + if (TESTBIT(d,6)) { + lp1_sw = 0; + if (lp1_sw_intr_ena && lp1_sw != old_sw1) + lpsw_irq = 1; + } + if (lphit_irq || lpsw_irq) + vt_lpen_intr(); + } +} + +int32 +vt11_get_nr(void) +{ INIT + return (name_flag<<15) | (search<<12) | name; +} + +void +vt11_set_nr(uint16 d) +{ INIT + DEBUGF(("set NR: no effect\r\n")); +} + +int32 +vt11_get_sdr(void) +{ + struct frame *p; + INIT + p = &stack[GETFIELD(stack_sel,4,2)]; + switch (GETFIELD(stack_sel,1,0)) { /* 16-bit "byte" within frame */ + case 0: + return p->_dpc; /* DPC bit#0 is always 0 */ + + case 1: + return (p->_name << 4) | p->_mode; + + case 2: + return (p->_italics << 15) | (p->_vscale << 11) | (p->_cscale << 9) | + (p->_crotate << 7) | (p->_intens << 4) | ((int)p->_color << 2) | + p->_ltype; + + case 3: + return (p->_blink << 15) | (p->_so << 14) | (p->_menu << 13) | + (p->_cesc << 12) | (p->_edgeintr << 11) | (p->_zdata << 10) | + (p->_depth << 8) | (p->_lp1swintr << 7) | + (p->_lp0swintr << 6) | (p->_lp1intr << 5) | (p->_inten1 << 4) | + (p->_lp0intr << 3) | (p->_inten0 << 2) | (p->_bright << 1) | + p->_stopintr; /* XXX should that be !p->_so ? */ + } + /*NOTREACHED*/ +} + +void +vt11_set_sdr(uint16 d) +{ INIT + DEBUGF(("set SDR: no effect\r\n")); +} + +int32 +vt11_get_str(void) +{ INIT + return char_term; +} + +void +vt11_set_str(uint16 d) +{ INIT + if (TESTBIT(d,7)) + char_term = GETFIELD(d,6,0); +} + +int32 +vt11_get_sar(void) +{ + int32 ret; + INIT + ret = (maint4<<15) | (maint3<<14) | (maint2<<13) | (maint1<<12) | + (offset<<10) | (jsr<<9) | stack_sel /*includes bit 5, TOS [level 8]*/; + switch (word_number) { + case 0: + ret |= 1<<6; + break; + case 1: + ret |= 1<<7; + break; + case 2: + ret |= 1<<8; + break; + /* others not reportable */ + } + return ret; +} + +void +vt11_set_sar(uint16 d) +{ INIT + maint4 = TESTBIT(d,15); /* 1 => synch. processing pipeline */ + maint3 = TESTBIT(d,14); /* 1 => copy delta,tangent to x,y pos */ + maint2 = TESTBIT(d,13); /* 1 => set single-step mode */ + maint1 = TESTBIT(d,12); /* 1 => vt11_get_dpc will return bdb */ + if (TESTBIT(d,5)) { + sp = &stack[8]; /* reset stack pointer */ + stack_sel = 8<<2; /* TOS amounts to level 8 */ +#if 1 /* the following seems wrong, but is needed to pass diagnostic test! */ + stack_sel |= 1; +#endif + } else { + stack_sel = GETFIELD(d,4,0); + sp = &stack[GETFIELD(stack_sel,4,2)]; + } +} + +/* registers used with the VS60 depth cueing option */ + +/* + * Since there is no support for hardware 3D rotation or viewing transform, the + * only effect of the Z coordinate is to modulate beam intensity along a vector + * to give the illusion that greater Z coordinates are closer (brighter). + * This is known as "depth cueing" and is implemented in illum3(). + */ + +int32 +vt11_get_zpr(void) +{ + int32 pos; + INIT + pos = lphit_irq ? lp_zpos : edge_irq ? edge_zpos : PNORM(zpos); + return GETFIELD(ABS(pos)/4,11,0); /* sign not reported? */ +} + +void +vt11_set_zpr(uint16 d) +{ INIT + DEBUGF(("set ZPR: no effect\r\n")); +} + +int32 +vt11_get_zor(void) +{ + int32 off, ret; + INIT + off = PNORM(zoff); + ret = GETFIELD(ABS(off),11,0); + if (s_xoff) /* (VT48 manual has this confused) */ + ret |= 1<<15; + if (s_yoff) /* (VT48 manual has this confused) */ + ret |= 1<<14; + if (s_zoff) + ret |= 1<<13; + return ret; +} + +void +vt11_set_zor(uint16 d) +{ INIT + zoff = PSCALE(GETFIELD(d,11,0)); + if (s_zoff = TESTBIT(d,13)) + zoff = -zoff; +} + +void +vt11_reset(void) +{ + /* XXX call display layer? destroy window? */ + + /* make sure display code has been initialized */ + if (!vt11_init) /* (SIMH invokes before display type is set) */ + return; /* wait until last moment */ + + if (VS60) { + /* XXX verify that this is correct VS60 character spacing */ + vt11_csp_w = 14; /* override VT11 options */ + vt11_csp_h = 24; + } /* else assume already set up for desired VT11 behavior */ + + x_edge = display_xpoints() - 1; + y_edge = display_ypoints() - 1; + reduce = display_scale(); + + /* reset VT11/VT48 to initial default internal state: */ + + /* clear interrupts, BDB, etc. */ + vt11_set_dpc(0); /* important! do this first */ + /* some of the following should probably be moved to vt11_set_dpc([even]) */ + internal_stop = int0_scope = 1; /* idle, console 0 enabled */ + lp0_sw_state = lp0_sw; /* sync with mouse button #1 */ + lp1_sw = 0; + shift_out = int1_scope = stop_intr_ena = blink_off = 0; + italics = blink_ena = char_rotate = menu = search = offset = 0; + lp0_sw_intr_ena = lp1_sw_intr_ena = lp0_intr_ena = lp1_intr_ena = 0; + file_z_data = edge_intr_ena = depth_cue_proc = char_escape = 0; + maint1 = maint2 = maint3 = maint4 = 0; + refresh_rate = 0; + char_buf = char_term = 0; + assoc_name = name = 0; + reloc = 0; + xpos = ypos = zpos = xoff = yoff = zoff = 0; + s_xoff = s_yoff = s_zoff = 0; + graphplot_step = 0; + graphic_mode = CHAR; + line_type = SOLID; + color = GREEN; + lp_intensify = 1; + cs_index = 1; + char_scale = vector_scale = 4; + intensity = 4; + + /* following just in case the stack is inspected via stack data reg. */ + { int i; + for (i = 0; i < 8; ++i) + memset(&stack[i], 0, sizeof(struct frame)); + } +} + +/* VS60 display subroutine support (see stack layout for SDR, above) */ + +static void +push() +{ + stack_over = STACK_FULL; + if (!stack_over) { + --sp; + sp[0] = sp[1]; /* initially have same parameters */ + /* (including *old* DPC) */ + stack_sel -= 1<<2; + /* XXX should stack_sel stack-byte bits be cleared? */ + } + /* else will generate interrupt soon after return */ +} + +static void +pop(int restore) +{ + stack_under = STACK_EMPTY; + if (!stack_under) { + ++sp; /* that's all! */ + stack_sel += 1<<2; + /* XXX should stack_sel stack-byte bits be cleared? */ + } + /* else will generate interrupt soon after return */ +} + +/* illuminate pixel in raster image */ + +static void +illum3(int32 x, int32 y, int32 z) /* XXX should supply intensity also */ + /* virtual CRT units (offset and normalized) */ +{ + int i; + + /* don't update position registers! */ + + /* coords might be outside viewable area, e.g. clipped italic character */ + if (!ONCRT(x, y) || !int0_scope) + return; + + if (blink_ena && blink_off) /* blinking & in dark phase */ + return; + + i = intensity; + if (depth_cue_proc) { /* apply depth cue */ + i += i * z / 1024; /* XXX is z scaled properly? */ + if (i > 7) + i = 7; + else if (i < 0) + i = 0; + } + i += DISPLAY_INT_MAX - 7; + if (i < DISPLAY_INT_MIN) + i = DISPLAY_INT_MIN; + + if (display_point((int)x, (int)y, i, 0) /* XXX VS60 might switch color */ + /* VT11, per maintenance spec, has threshold 6 for CHAR, 4 for others */ + /* but the classic Lunar Lander uses 3 for its menu and thrust bar! */ + /* I seem to recall that both thresholds were 4 for the VS60 (VR48). */ +#if 0 + && (i >= (DISPLAY_INT_MAX-1) /* (using i applies depth cueing) */ + || (graphic_mode != CHAR && i >= (DISPLAY_INT_MAX-3))) +#else + /* The following imposes thresholds of 3 for all graphic objects. */ + && (i >= (DISPLAY_INT_MAX-4)) /* (using i applies depth cueing) */ +#endif + && !lp_suppress) { + lp0_flag = 1; + if (lp0_intr_ena) + lphit_irq = 1; /* will lead to an interrupt */ + /* + * Save LP hit coordinates so CPU can look at them; the virtual position + * registers cannot be reported on LP interrupt, since they track the + * (pre-clipping) end of the vector that was being drawn. + */ + lp_xpos = x; + if (menu) + lp_xpos -= MENU_OFFSET; + lp_ypos = y; + lp_zpos = z; + if (lp_intensify) /* [technically shouldn't exceed max] */ + display_point((int)x, (int)y, DISPLAY_INT_MAX, 0); + /* XXX appropriate for VT11; what about VS60? chars? */ + } +} + +#define illum2(x,y) illum3(x, y, PNORM(zpos)) /* may be depth cued */ + /* the extra overhead if not depth cueing is not much */ + +static void +point3(int i, int32 x1, int32 y1, int32 z1, int detect_edge) + /* unscaled, unoffset display-file units */ +{ + int32 x0 = PNORM(xpos), y0 = PNORM(ypos); + + if (detect_edge) { + edge_indic = ONSCREEN(x0, y0); /* first test */ + edge_flag = !ONSCREEN(x0, y0); /* first test */ + } else { + edge_indic = 0; + edge_flag = 0; + } + xpos = VSCALE(x1) + xoff; + ypos = VSCALE(y1) + yoff; + zpos = VSCALE(z1 * 4) + zoff; + x1 = PNORM(xpos); + y1 = PNORM(ypos); + z1 = PNORM(zpos); + if (detect_edge) { + edge_indic &= !ONSCREEN(x1, y1); /* second test */ + edge_flag &= ONSCREEN(x1, y1); /* second test */ + edge_flag |= edge_indic; + if (edge_flag && edge_intr_ena) { + edge_xpos = x1; + edge_ypos = y1; + edge_zpos = z1; + edge_irq = 1; +#if 1 /* XXX uncertain whether point is displayed during edge intr. */ + return; /* point not displayed */ +#endif + } + } + if (i && ONSCREEN(x1, y1)) + if (menu) + illum3(x1 + MENU_OFFSET, y1, z1); + else + illum3(x1, y1, z1); +} + +#define point2(i,x,y,e) point3(i, x, y, PNORM(zpos - zoff) / 4, e) + /* the extra overhead if not depth cueing is not much */ + +/* 4 bit counter, fed from div 2 clock (to compensate for raster algorithm) */ +/* XXX check display against example photos to see if div 2 is right */ +static unsigned char line_counter; +#define LC1 02 +#define LC2 04 +#define LC3 010 +#define LC4 020 + +/* point on a line (apply line style) */ +static void +lpoint(int32 x, int32 y) /* window-system screen pixel units */ +{ + int i, on; + + /* convert back from actual screen pixels to emulated CRT coordinates */ + x *= reduce; + y *= reduce; + + if (!(on = (line_type == SOLID) || stroking)) + for (i = 0; i < reduce; ++i) { + switch (line_type) { + case LONG_DASH: + if (line_counter & LC4) + on = 1; + break; + case SHORT_DASH: + if (line_counter & LC3) + on = 1; + break; + case DOT_DASH: + /* LC(2:1)H * LC3L + LC4L */ + if (((line_counter & (LC1|LC2)) == (LC1|LC2) + && !(line_counter & LC3)) || !(line_counter & LC4)) + on = 1; + break; + } + + --line_counter; + } + + if (on) + illum2(x, y); +} + +/* + * 2-step algorithm, developed by Xiaolin Wu + * from http://graphics.lcs.mit.edu/~mcmillan/comp136/Lecture6/Lines.html + * + * The two-step algorithm takes the interesting approach of treating + * line drawing as a automaton, or finite state machine. If one looks + * at the possible configurations that the next two pixels of a line, + * it is easy to see that only a finite set of possiblities exist. + * If line styles weren't involved, the line could be drawn symmetrically + * from both ends toward the midpoint. + * Rasterization is done using actual screen pixel units, not emulated device + * coordinates! + */ + +static void +lineTwoStep(int32 x0, int32 y0, int32 x1, int32 y1) + /* virtual CRT units (offset and normalized) */ +{ + int32 dx, dy; + int stepx, stepy; + + /* after clipping is implemented, coords should always be on-screen */ + + /* convert from emulated CRT units to actual screen pixels */ + x0 /= reduce; + y0 /= reduce; + x1 /= reduce; + y1 /= reduce; + + dx = x1 - x0; + dy = y1 - y0; + + /* XXX there could be fast special cases for "basic vectors" */ + + if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } + if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } + +#define TPOINT do { if (lphit_irq && !stroking) goto hit; \ + /* XXX would longjmp be more efficient? */ \ + lpoint(x0, y0); \ + } while (0) + + if (!skip_start) /* not for continuing stroke when VS60 char. or arc */ + lpoint(x0, y0); /* (could have used TPOINT) */ + + if (dx == 0 && dy == 0) /* following algorithm won't work */ + return; /* just the one dot */ + + if (dx > dy) { + int32 length = (dx - 1) / 2; + int extras = (dx - 1) & 1; + int32 incr2 = (dy * 4) - (dx * 2); + if (incr2 < 0) { + int32 c = dy * 2; + int32 incr1 = c * 2; + int32 d = incr1 - dx; + int32 i; + for (i = 0; i < length; i++) { + x0 += stepx; + if (d < 0) { /* Pattern: */ + TPOINT; /* x o o */ + x0 += stepx; + TPOINT; + d += incr1; + } + else { + if (d < c) { /* Pattern: */ + TPOINT; /* o */ + y0 += stepy; /* x o */ + } else { /* Pattern: */ + y0 += stepy; /* o o */ + TPOINT; /* x */ + } + x0 += stepx; + TPOINT; + d += incr2; + } + } + if (extras > 0) { + x0 += stepx; + if (d >= c) + y0 += stepy; + TPOINT; + } + + } else { + int32 c = (dy - dx) * 2; /* negative */ + int32 incr1 = c * 2; /* negative */ + int32 d = incr1 + dx; + int32 i; + for (i = 0; i < length; i++) { + x0 += stepx; + if (d > 0) { /* Pattern: */ + y0 += stepy; /* o */ + TPOINT; /* o */ + x0 += stepx; /* x */ + y0 += stepy; + TPOINT; + d += incr1; + } else { + if (d < c) { /* Pattern: */ + TPOINT; /* o */ + y0 += stepy; /* x o */ + } else { /* Pattern: */ + y0 += stepy; /* o o */ + TPOINT; /* x */ + } + x0 += stepx; + TPOINT; + d += incr2; + } + } + if (extras > 0) { + x0 += stepx; + if (d >= c) + y0 += stepy; + TPOINT; + } + } + + } else { + int32 length = (dy - 1) / 2; + int extras = (dy - 1) & 1; + int32 incr2 = (dx * 4) - (dy * 2); + if (incr2 < 0) { + int32 c = dx * 2; + int32 incr1 = c * 2; + int32 d = incr1 - dy; + int32 i; + for (i = 0; i < length; i++) { + y0 += stepy; + if (d < 0) { /* Pattern: */ + TPOINT; /* o */ + y0 += stepy; /* o */ + TPOINT; /* x */ + d += incr1; + } else { + if (d < c) { /* Pattern: */ + TPOINT; /* o */ + x0 += stepx; /* o */ + /* x */ + } else { /* Pattern: */ + x0 += stepx; /* o */ + TPOINT; /* o */ + /* x */ + } + y0 += stepy; + TPOINT; + d += incr2; + } + } + if (extras > 0) { + y0 += stepy; + if (d >= c) + x0 += stepx; + TPOINT; + } + + } else { + int32 c = (dx - dy) * 2; /* nonpositive */ + int32 incr1 = c * 2; /* nonpositive */ + int32 d = incr1 + dy; + int32 i; + for (i = 0; i < length; i++) { + y0 += stepy; + if (d > 0) { /* Pattern: */ + x0 += stepx; + TPOINT; /* o */ + y0 += stepy; /* o */ + x0 += stepx; /* x */ + TPOINT; + d += incr1; + } else { + if (d < c) { /* Pattern: */ + TPOINT; /* o */ + x0 += stepx; /* o */ + /* x */ + } else { /* Pattern: */ + x0 += stepx; /* o */ + TPOINT; /* o */ + /* x */ + } + y0 += stepy; + TPOINT; + d += incr2; + } + } + if (extras > 0) { + y0 += stepy; + if (d >= c) + x0 += stepx; + TPOINT; + } + } + } + lpoint(x1, y1); /* not TPOINT (0-length vector on resume) */ + return; + + /* here if LP hit interrupt during rendering */ + hit: + more_vect = 1; + save_x0 = x0 * reduce; + save_y0 = y0 * reduce; + save_x1 = x1 * reduce; + save_y1 = y1 * reduce; + /* XXX should also save Z coord. for use when completing the line */ + /* line_counter is static and thus will be intact upon resume */ +} /* lineTwoStep */ + +/* draw a 3D relative vector, depth-cued when appropriate */ + +static void +vector3(int i, int32 dx, int32 dy, int32 dz) /* unscaled display-file units */ +{ + int32 x0, y0, z0, x1, y1, z1; + int on0, on1; /* ONSCREEN(x0,y0), ONSCREEN(x1,y1) */ + + line_counter = 037; /* reset line-style counter */ + + dx = VSCALE(dx); /* apply vector scale (VS60) */ + dy = VSCALE(dy); + dz = VSCALE(dz * 4); + x0 = PNORM(xpos); /* (includes offset) */ + y0 = PNORM(ypos); + z0 = PNORM(zpos); + xpos += dx; + ypos += dy; + zpos += dz; + x1 = PNORM(xpos); + y1 = PNORM(ypos); + z1 = PNORM(zpos); + dx = PNORM(dx); + dy = PNORM(dy); + dz = PNORM(dz); + DEBUGF(("offset, normalized vector i%d (%ld,%ld,%ld) to (%ld,%ld,%ld)\r\n", + i, (long)x0, (long)y0, (long)z0, (long)x1, (long)y1, (long)z1)); + + /* Maintenance Switch 3 => store delta length and tangent in xpos,ypos */ + if (maint3) { + int32 adx = ABS(dx), ady = ABS(dy); + if (adx == ady) { + xpos = adx; /* or ady */ + ypos = 07777; /* (12 bits?) ~ 1.0 */ + } else if (adx > ady) { + xpos = dx; + ypos = 010000L * ady / adx; /* truncates */ /* XXX 07777L? */ + } else /* (adx < ady) */ { + xpos = dy; + ypos = 010000L * adx / ady; /* truncates */ /* XXX 07777L? */ + } + DEBUGF(("delta=0%o, tangent=0%o\r\n", xpos, ypos)); + xpos = PSCALE(xpos); /* compensates for eventual PNORM */ + ypos = PSCALE(ypos); /* compensates for eventual PNORM */ + } + + /* clip to viewport ("working surface") if necessary */ + + /* XXX not implemented yet */ + + /* check for edge conditions (XXX will change when clipping implemented) */ + on0 = ONSCREEN(x0, y0); + on1 = ONSCREEN(x1, y1); + edge_indic = on0 && !on1; + edge_flag = edge_indic || (!on0 && on1); + if (edge_flag && edge_intr_ena) { /* need to clip to viewport */ + /* XXX edge positions aren't right; need proper clipping, + then recompute using tangent register */ + edge_xpos = x1; + edge_ypos = y1; + edge_zpos = z1; + return; + } + + if (dx == 0 && dy == 0) + return; /* hardware skips null vector */ + + /* XXX for now, resort to scissoring: + illuminates only pixels that lie in the visible display area */ + + /* draw OK even when Maintenance Switch 3 is set */ + /* (but updated position registers must not be used to draw vector) */ + if (i && int0_scope) + if (menu) + lineTwoStep(x0 + MENU_OFFSET, y0, x1 + MENU_OFFSET, y1); + else + lineTwoStep(x0, y0, x1, y1); + /* XXX Depth cueing not yet right. Probably the way to do this is to + adapt a copy of lineTwoStep() to step intensity values along the + vector as it rasterizes it, calling illum3() instead of illum2(). + The unmodified lineTwoStep() should continue to be used for 2D + vectors, to avoid the overhead of recomputing constant i values. */ + + /* + * In case of LP hit, recompute coords using "tangent register", because: + * (1) pixelization can lead to off-by-1 or -2 + * (2) rasterization might not be same as VT48 computation + */ + + if (lp0_flag) { + long tangent; + int32 adx = ABS(dx), ady = ABS(dy); + if (adx >= ady) { + tangent = 010000L * dy / dx; /* signed */ + lp_ypos = y0 + tangent * (lp_xpos - x0) / 010000L; + tangent = 010000L * dz / dx; + lp_zpos = z0 + tangent * (lp_xpos - x0) / 010000L; + } else { + tangent = 010000L * dx / dy; /* signed */ + lp_xpos = x0 + tangent * (lp_ypos - y0) / 010000L; + tangent = 010000L * dz / dy; + lp_zpos = z0 + tangent * (lp_ypos - y0) / 010000L; + } + DEBUGF(("adjusted LP coords (0%o,0%o)\r\n", lp_xpos, lp_ypos)); + /* xpos,ypos,zpos still pertain to the original endpoint + (assuming that Maintenance Switch 3 isn't set) */ + } +} + +/* draw a 2D relative vector, depth-cued (constant Z) when appropriate */ + +/* for the sake of efficiency, vector2() does not invoke vector3() */ + +static void +vector2(int i, int32 dx, int32 dy) /* unscaled display-file units */ +{ + int32 x0, y0, x1, y1; + int on0, on1; /* ONSCREEN(x0,y0), ONSCREEN(x1,y1) */ + + dx = stroking ? CSCALE(dx) : VSCALE(dx); /* apply scale factor (VS60) */ + dy = stroking ? CSCALE(dy) : VSCALE(dy); + x0 = PNORM(xpos); /* (includes offset) */ + y0 = PNORM(ypos); + xpos += dx; + ypos += dy; + x1 = PNORM(xpos); + y1 = PNORM(ypos); + dx = PNORM(dx); + dy = PNORM(dy); + + if (stroking) { /* drawing a VS60 character */ + DEBUGF(("offset, normalized stroke i%d (%ld,%ld) to (%ld,%ld)\r\n", + i, (long)x0,(long)y0, (long)x1,(long)y1)); + + if (dx == 0 && dy == 0) { /* just display a point */ + if (menu) + illum2(x0 + MENU_OFFSET, y0); /* [checks ONCRT, int0_scope] */ + else + illum2(x0, y0); /* [checks ONCRT, int0_scope] */ + return; + } + } else { + DEBUGF(("[offset, normalized] vector i%d (%ld,%ld) to (%ld,%ld)\r\n", + i, (long)x0,(long)y0, (long)x1,(long)y1)); + + line_counter = 037; /* reset line-style counter */ + + /* Maintenance Switch 3 => store delta length,tangent in xpos,ypos */ + if (maint3) { + int32 adx = ABS(dx), ady = ABS(dy); + if (adx == ady) { + xpos = adx; /* or ady */ + ypos = 07777; /* (12 bits?) ~ 1.0 */ + } else if (adx > ady) { + xpos = dx; + ypos = 010000L * ady / adx; /* truncates *//* XXX 07777L? */ + } else /* (adx < ady) */ { + xpos = dy; + ypos = 010000L * adx / ady; /* truncates *//* XXX 07777L? */ + } + DEBUGF(("delta=0%o, tangent=0%o\r\n", xpos, ypos)); + xpos = PSCALE(xpos); /* compensates for eventual PNORM */ + ypos = PSCALE(ypos); /* compensates for eventual PNORM */ + } + + /* clip to viewport ("working surface") if necessary */ + + /* XXX not implemented yet */ + + /* check for edge conditions (XXX changes when clipping implemented) */ + on0 = ONSCREEN(x0, y0); + on1 = ONSCREEN(x1, y1); + edge_indic = on0 && !on1; + edge_flag = edge_indic || (!on0 && on1); + if (edge_flag && edge_intr_ena) { /* need to clip to viewport */ + /* XXX edge positions aren't right; need proper clipping, + then recompute using tangent register */ + edge_xpos = x1; + edge_ypos = y1; + edge_zpos = PNORM(zpos); + return; + } + + if (dx == 0 && dy == 0) + return; /* hardware skips null vectors */ + } + + /* XXX for now, resort to scissoring: + illuminates only pixels that lie in the visible display area */ + + /* draw OK even when Maintenance Switch 3 is set */ + /* (but updated position registers must not be used to draw vector) */ + if (i && int0_scope) + if (menu) + lineTwoStep(x0 + MENU_OFFSET, y0, x1 + MENU_OFFSET, y1); + else + lineTwoStep(x0, y0, x1, y1); + + /* + * In case of LP hit, recompute coords using "tangent register", because: + * (1) distinct virtual CRT points can be mapped into the same pixel + * (2) raster computation might not match that of the actual VT48 + */ + + if (lp0_flag) { + long tangent; + int32 adx = ABS(dx), ady = ABS(dy); + if (adx >= ady) { + tangent = 010000L * dy / dx; /* signed */ + lp_ypos = y0 + tangent * (lp_xpos - x0) / 010000L; + } else { + tangent = 010000L * dx / dy; /* signed */ + lp_xpos = x0 + tangent * (lp_ypos - y0) / 010000L; + } + DEBUGF(("adjusted LP coords (0%o,0%o)\r\n", lp_xpos, lp_ypos)); + /* xpos,ypos,zpos still pertain to the original endpoint + (assuming that Maintenance Switch 3 isn't set) */ + } +} + +/* basic vector (multiple of 45 degrees; directions numbered CCW, #0 => +X) */ +static void +basic_vector(int i, int dir, int len) /* unscaled display-file units */ +{ + int32 dx, dy; + + /* Alternatively, could be rasterized specially for each case; then + the general vector2() function could detect these special cases and + invoke this function to handle them, instead of the other way around. */ + + switch (dir) { + case 0: + dx = len; + dy = 0; + break; + case 1: + dx = len; + dy = len; + break; + case 2: + dx = 0; + dy = len; + break; + case 3: + dx = -len; + dy = len; + break; + case 4: + dx = -len; + dy = 0; + break; + case 5: + dx = -len; + dy = -len; + break; + case 6: + dx = 0; + dy = -len; + break; + case 7: + dx = len; + dy = -len; + break; + default: /* "can't happen" */ + DEBUGF(("BUG: basic vector: illegal direction %d\r\n", dir)); + return; + } + DEBUGF(("basic ")); + vector2(i, dx, dy); +} + +/* + * support for VS60 circle/arc option + * + * Since the literature that I have access to does not handle the case where + * starting and ending radii differ, I invented a solution that should be + * "good enough" for now: an approximation of an Archimedean spiral is drawn + * as connected individual chords, with the line-type counter applied (without + * being reset) over the entire curve. + * + * It is not known whether the direction is supposed to be clockwise or + * counterclockwise (the latter is assumed in the following code); it is + * assumed that if the starting and ending directions from the center point + * are identical, that a full circle is being specified. + * + * Although throughout the display simulation substantial effort has been + * invested to avoid using floating point, this preliminary implementation + * of the circle/arc generator does use floating point. Presumably this + * is avoidable, but the algorithmic details would need to be worked out. + * If use of floating point is a problem, #define NO_CONIC_OPT when compiling. + */ + +static void +conic2(int i, int32 dcx, int32 dcy, int32 dex, int32 dey) + /* unscaled display-file units */ +{ +#ifdef NO_CONIC_OPT + /* just draw vector to endpoint (like real VS60 with option missing) */ + vector2(i, dex, dey); +#else + int32 xs, ys, xc, yc, xe, ye, x, y, n; + double rs, re, dr, as, da; + int ons, one; /* ONSCREEN(xs,ys), ONSCREEN(xe,ye) */ + static double two_pi = -1.0; /* will be set (once only) to 2*Pi */ + static double k; /* will be set to 2-sqrt(4-(Pi/4)^2) */ + + if (two_pi < 0.0) { /* (initial entry only) */ + k = atan2(1.0, 1.0); + two_pi = 8.0 * k; + k = 2.0 - sqrt(4.0 - k*k); + } + dcx = VSCALE(dcx); /* apply vector scale factor */ + dcy = VSCALE(dcy); + dex = VSCALE(dex); + dey = VSCALE(dey); + xs = PNORM(xpos); /* starting pos. (includes offset) */ + ys = PNORM(ypos); + xc = PNORM(xpos + dcx); /* center pos. (includes offset) */ + yc = PNORM(ypos + dcy); + xe = PNORM(xpos + dex); /* ending pos. (includes offset) */ + ye = PNORM(ypos + dey); + /* determine vector from center to finish */ + dex -= dcx; /* PSCALEd */ + dey -= dcy; + + DEBUGF(("offset, normalized arc i%d s(%ld,%ld) c(%ld,%ld) e(%ld,%ld)\r\n", + i, (long)xs,(long)ys, (long)xc,(long)yc, (long)xe,(long)ye)); + + /* XXX not known whether Maintenance Switch 3 has any effect for arcs */ + + /* clip to viewport ("working surface") if necessary */ + + /* XXX not implemented yet [could check each chord individually] */ + + /* check for edge conditions (XXX changes when clipping implemented) */ + /* XXX this test is very crude; should be much more complex */ + ons = ONSCREEN(xs, ys); + one = ONSCREEN(xe, ye); + edge_indic = ons && !one; + edge_flag = edge_indic || (!ons && one); + if (edge_flag && edge_intr_ena) { /* need to clip to viewport */ + /* XXX edge positions aren't right; need proper clipping */ + edge_xpos = xe; + edge_ypos = ye; + edge_zpos = PNORM(zpos); + goto done; + } + + /* XXX for now, resort to scissoring: + illuminates only pixels that lie in the visible display area */ + + if (dcx == 0 && dcy == 0 && dex == 0 && dey == 0) + goto done; /* skip null curve */ + + /* determine starting, ending radii and their maximum */ + rs = PNORM(sqrt((double)dcx*dcx + (double)dcy*dcy)); /* (f.p.) */ + re = PNORM(sqrt((double)dex*dex + (double)dey*dey)); + dr = rs >= re ? rs : re; + + /* determine starting direction from center, and included angle */ + as = dcx == 0 && dcy == 0 ? 0.0 : atan2((double)-dcy, (double)-dcx); + da = (dex == 0 && dey == 0 ? 0.0 : atan2((double)dey, (double)dex)) - as; + while (da <= 0.0) /* exactly 0.0 implies full cycle */ + da += two_pi; + + /* determine number of chords to use; + make deviation from true curve no more than approximately one pixel */ + dr = reduce / dr; + if (dr > k) + dr = k; + n = (int32)(da / sqrt(4.0*dr - dr*dr) + 1.0); + if (n < 1) /* "can't happen" */ + n = 1; + else if (n > 360) + n = 360; /* arbitrarily chosen upper limit */ + + /* determine angular and radial step sizes */ + dr = (re - rs) / n; + da /= n; + + if (menu) { + xs += MENU_OFFSET; + xc += MENU_OFFSET; + xe += MENU_OFFSET; + } + + line_counter = 037; /* reset line-style counter */ + + /* draw successive chords */ + while (--n > 0) { + rs += dr; + as += da; + re = rs * cos(as); + x = xc + (re >= 0 ? (int32)(re + 0.5) : -(int32)(-re + 0.5)); + re = rs * sin(as); + y = yc + (re >= 0 ? (int32)(re + 0.5) : -(int32)(-re + 0.5)); + lineTwoStep(xs, ys, x, y); /* (applies continuing line style) */ + skip_start = 1; /* don't double-illuminate junctions */ + xs = x; + ys = y; + if (lphit_irq) + goto done; /* light-pen hit interrupted drawing */ + } + lineTwoStep(xs, ys, xe, ye); /* draw final chord to exact endpoint */ + skip_start = 0; /* important! */ + + done: + xpos += dcx + dex; /* update virtual beam position */ + ypos += dcy + dey; + if (lp0_flag) { + DEBUGF(("LP hit on arc at (0%o,0%o)\r\n", lp_xpos, lp_ypos)); + if (lphit_irq) { + /* XXX save parameters for drawing remaining chords */ + } + } +#endif +} + +static void +conic3(int i, int32 dcx, int32 dcy, int32 dcz, int32 dex, int32 dey, int32 dez) + /* unscaled display-file units */ +{ +#ifdef NO_CONIC_OPT + /* just draw vector to endpoint (like real VS60 with option missing) */ + vector3(i, dex, dey, dez); +#else + conic2(i, dcx, dcy, dex, dey); /* XXX not properly depth cued */ + zpos += PSCALE(dez); +#endif +} + +/* + * VT11 character font; + * 6x8 matrix, not serpentine encoded, decenders supported as in real VT11 + */ + +static const unsigned char dots[0200][6] = { + { 0x8f, 0x50, 0x20, 0x10, 0x08, 0x07 }, /* 000 lambda */ + { 0x1e, 0x21, 0x22, 0x14, 0x0c, 0x13 }, /* 001 alpha */ + { 0x00, 0x18, 0x24, 0xff, 0x24, 0x18 }, /* 002 phi */ + { 0x83, 0xc5, 0xa9, 0x91, 0x81, 0xc3 }, /* 003 SIGMA */ + { 0x00, 0x46, 0xa9, 0x91, 0x89, 0x06 }, /* 004 delta */ + { 0x03, 0x05, 0x09, 0x11, 0x21, 0x7f }, /* 005 DELTA */ + { 0x00, 0x20, 0x20, 0x3f, 0x01, 0x01 }, /* 006 iota */ + { 0x46, 0x29, 0x11, 0x2e, 0x40, 0x80 }, /* 007 gamma */ + { 0x7f, 0x80, 0x80, 0x80, 0x80, 0x7f }, /* 010 intersect */ + { 0x40, 0x3c, 0x04, 0xff, 0x04, 0x78 }, /* 011 psi */ + { 0x00, 0x10, 0x10, 0x54, 0x10, 0x10 }, /* 012 divide by */ + { 0x00, 0x60, 0x90, 0x90, 0x60, 0x00 }, /* 013 degree */ + { 0x00, 0x01, 0x00, 0x10, 0x00, 0x01 }, /* 014 therefore */ + { 0x01, 0x02, 0x3c, 0x02, 0x02, 0x3c }, /* 015 mu */ + { 0x11, 0x7f, 0x91, 0x81, 0x41, 0x03 }, /* 016 pound sterling */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* 017 SHIFT IN */ + { 0x20, 0x40, 0x7f, 0x40, 0x7f, 0x40 }, /* 020 pi */ + { 0x00, 0xff, 0x00, 0x00, 0xff, 0x00 }, /* 021 parallel */ + { 0x1d, 0x23, 0x40, 0x42, 0x25, 0x19 }, /* 022 OMEGA */ + { 0x1c, 0x22, 0x61, 0x51, 0x4e, 0x40 }, /* 023 sigma */ + { 0x20, 0x40, 0x40, 0x7f, 0x40, 0x40 }, /* 024 UPSILON */ + { 0x00, 0x1c, 0x2a, 0x49, 0x49, 0x00 }, /* 025 epsilon */ + { 0x10, 0x38, 0x54, 0x10, 0x10, 0x10 }, /* 026 left arrow */ + { 0x10, 0x10, 0x10, 0x54, 0x38, 0x10 }, /* 027 right arrow */ + { 0x00, 0x20, 0x40, 0xfe, 0x40, 0x20 }, /* 030 up arrow */ + { 0x00, 0x04, 0x02, 0x7f, 0x02, 0x04 }, /* 031 down arrow */ + { 0x00, 0xff, 0x80, 0x80, 0x80, 0x80 }, /* 032 GAMMA */ + { 0x00, 0x01, 0x01, 0xff, 0x01, 0x01 }, /* 033 perpendicular */ + { 0x2a, 0x2c, 0x28, 0x38, 0x68, 0xa8 }, /* 034 unequal */ + { 0x24, 0x48, 0x48, 0x24, 0x24, 0x48 }, /* 035 approx equal */ + { 0x00, 0x20, 0x10, 0x08, 0x10, 0x20 }, /* 036 vel */ + { 0xff, 0x81, 0x81, 0x81, 0x81, 0xff }, /* 037 box */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* 040 space */ + { 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00 }, /* 041 ! */ + { 0x00, 0xe0, 0x00, 0x00, 0xe0, 0x00 }, /* 042 " */ + { 0x00, 0x24, 0xff, 0x24, 0xff, 0x24 }, /* 043 # */ + { 0x22, 0x52, 0xff, 0x52, 0x4c, 0x00 }, /* 044 $ */ + { 0x42, 0xa4, 0x48, 0x12, 0x25, 0x42 }, /* 045 % */ + { 0x66, 0x99, 0x99, 0x66, 0x0a, 0x11 }, /* 046 & */ + { 0x00, 0x00, 0x20, 0x40, 0x80, 0x00 }, /* 047 ' */ + { 0x00, 0x00, 0x3c, 0x42, 0x81, 0x00 }, /* 050 ( */ + { 0x00, 0x00, 0x81, 0x42, 0x3c, 0x00 }, /* 051 ) */ + { 0x00, 0x44, 0x28, 0xf0, 0x28, 0x44 }, /* 052 * */ + { 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10 }, /* 053 + */ + { 0x00, 0x01, 0x06, 0x00, 0x00, 0x00 }, /* 054 , */ + { 0x00, 0x10, 0x10, 0x10, 0x10, 0x10 }, /* 055 - */ + { 0x00, 0x00, 0x06, 0x06, 0x00, 0x00 }, /* 056 . */ + { 0x02, 0x04, 0x08, 0x10, 0x20, 0x40 }, /* 057 / */ + { 0x7e, 0x85, 0x89, 0x91, 0xa1, 0x7e }, /* 060 0 */ + { 0x00, 0x41, 0xff, 0x01, 0x00, 0x00 }, /* 061 1 */ + { 0x47, 0x89, 0x91, 0x91, 0x91, 0x61 }, /* 062 2 */ + { 0x42, 0x81, 0x91, 0xb1, 0xd1, 0x8e }, /* 063 3 */ + { 0x0c, 0x14, 0x24, 0x44, 0xff, 0x04 }, /* 064 4 */ + { 0xf2, 0x91, 0x91, 0x91, 0x91, 0x8e }, /* 065 5 */ + { 0x3c, 0x46, 0x89, 0x89, 0x89, 0x46 }, /* 066 6 */ + { 0x40, 0x87, 0x88, 0x90, 0xa0, 0xc0 }, /* 067 7 */ + { 0x6e, 0x91, 0x91, 0x91, 0x91, 0x6e }, /* 070 8 */ + { 0x62, 0x91, 0x91, 0x91, 0x62, 0x3c }, /* 071 9 */ + { 0x00, 0x66, 0x66, 0x00, 0x00, 0x00 }, /* 072 : */ + { 0x00, 0x00, 0x61, 0x66, 0x00, 0x00 }, /* 073 ; */ + { 0x00, 0x18, 0x24, 0x42, 0x81, 0x00 }, /* 074 < */ + { 0x00, 0x28, 0x28, 0x28, 0x28, 0x28 }, /* 075 = */ + { 0x00, 0x81, 0x42, 0x24, 0x18, 0x00 }, /* 076 > */ + { 0x00, 0x40, 0x80, 0x9d, 0x90, 0x60 }, /* 077 ? */ + { 0x3c, 0x42, 0x91, 0xa9, 0xa9, 0x72 }, /* 100 @ */ + { 0x3f, 0x48, 0x88, 0x88, 0x48, 0x3f }, /* 101 A */ + { 0x81, 0xff, 0x91, 0x91, 0x91, 0x6e }, /* 102 B */ + { 0x3c, 0x42, 0x81, 0x81, 0x81, 0x42 }, /* 103 C */ + { 0x81, 0xff, 0x81, 0x81, 0x42, 0x3c }, /* 104 D */ + { 0x81, 0xff, 0x91, 0x91, 0x91, 0xc3 }, /* 105 E */ + { 0x81, 0xff, 0x91, 0x90, 0x80, 0xc0 }, /* 106 F */ + { 0x3c, 0x42, 0x81, 0x89, 0x89, 0x4f }, /* 107 G */ + { 0xff, 0x10, 0x10, 0x10, 0x10, 0xff }, /* 110 H */ + { 0x00, 0x81, 0xff, 0x81, 0x00, 0x00 }, /* 111 I */ + { 0x0e, 0x01, 0x01, 0x81, 0xfe, 0x80 }, /* 112 J */ + { 0xff, 0x08, 0x10, 0x28, 0x44, 0x83 }, /* 113 K */ + { 0x81, 0xff, 0x81, 0x01, 0x01, 0x03 }, /* 114 L */ + { 0xff, 0x40, 0x30, 0x30, 0x40, 0xff }, /* 115 M */ + { 0xff, 0x20, 0x10, 0x08, 0x04, 0xff }, /* 116 N */ + { 0x3c, 0x42, 0x81, 0x81, 0x42, 0x3c }, /* 117 O */ + { 0x81, 0xff, 0x90, 0x90, 0x90, 0x60 }, /* 120 P */ + { 0x3c, 0x42, 0x81, 0x8f, 0x42, 0x3d }, /* 121 Q */ + { 0x81, 0xff, 0x90, 0x98, 0x94, 0x63 }, /* 122 R */ + { 0x22, 0x51, 0x91, 0x91, 0x89, 0x46 }, /* 123 S */ + { 0xc0, 0x80, 0x81, 0xff, 0x81, 0xc0 }, /* 124 T */ + { 0xfe, 0x01, 0x01, 0x01, 0x01, 0xfe }, /* 125 U */ + { 0xff, 0x02, 0x04, 0x08, 0x10, 0xe0 }, /* 126 V */ + { 0xff, 0x02, 0x0c, 0x0c, 0x02, 0xff }, /* 127 W */ + { 0xc3, 0x24, 0x18, 0x18, 0x24, 0xc3 }, /* 130 X */ + { 0x00, 0xe0, 0x10, 0x0f, 0x10, 0xe0 }, /* 131 Y */ + { 0x83, 0x85, 0x89, 0x91, 0xa1, 0xc1 }, /* 132 Z */ + { 0x00, 0x00, 0xff, 0x81, 0x81, 0x00 }, /* 133 [ */ + { 0x00, 0x40, 0x20, 0x10, 0x08, 0x04 }, /* 134 \ */ + { 0x00, 0x00, 0x81, 0x81, 0xff, 0x00 }, /* 135 ] */ + { 0x00, 0x10, 0x20, 0x40, 0x20, 0x10 }, /* 136 ^ */ + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x00 }, /* 137 _ */ + /* for all lowercase characters, first column is just a "descender" flag: */ + { 0x00, 0x00, 0x80, 0x40, 0x20, 0x00 }, /* 140 ` */ + { 0x00, 0x26, 0x29, 0x29, 0x2a, 0x1f }, /* 141 a */ + { 0x00, 0xff, 0x12, 0x21, 0x21, 0x1e }, /* 142 b */ + { 0x00, 0x1e, 0x21, 0x21, 0x21, 0x12 }, /* 143 c */ + { 0x00, 0x1e, 0x21, 0x21, 0x12, 0xff }, /* 144 d */ + { 0x00, 0x1e, 0x29, 0x29, 0x29, 0x19 }, /* 145 e */ + { 0x00, 0x20, 0x7f, 0xa0, 0xa0, 0x80 }, /* 146 f */ + { 0x01, 0x78, 0x85, 0x85, 0x49, 0xfe }, /* 147 g */ + { 0x00, 0xff, 0x10, 0x20, 0x20, 0x1f }, /* 150 h */ + { 0x00, 0x00, 0x21, 0xbf, 0x01, 0x00 }, /* 151 i */ + { 0x01, 0x02, 0x01, 0x81, 0xfe, 0x00 }, /* 152 j */ + { 0x00, 0xff, 0x08, 0x14, 0x22, 0x21 }, /* 153 k */ + { 0x00, 0x00, 0xfe, 0x01, 0x01, 0x00 }, /* 154 l */ + { 0x00, 0x3f, 0x20, 0x3f, 0x20, 0x3f }, /* 155 m */ + { 0x00, 0x3f, 0x10, 0x20, 0x20, 0x1f }, /* 156 n */ + { 0x00, 0x1e, 0x21, 0x21, 0x21, 0x1e }, /* 157 o */ + { 0x01, 0xff, 0x48, 0x84, 0x84, 0x78 }, /* 160 p */ + { 0x01, 0x78, 0x84, 0x84, 0x48, 0xff }, /* 161 q */ + { 0x00, 0x3f, 0x08, 0x10, 0x20, 0x20 }, /* 162 r */ + { 0x00, 0x12, 0x29, 0x29, 0x29, 0x26 }, /* 163 s */ + { 0x00, 0x20, 0xfe, 0x21, 0x21, 0x00 }, /* 164 t */ + { 0x00, 0x3e, 0x01, 0x01, 0x02, 0x3f }, /* 165 u */ + { 0x00, 0x3c, 0x02, 0x01, 0x02, 0x3c }, /* 166 v */ + { 0x00, 0x3e, 0x01, 0x1e, 0x01, 0x3e }, /* 167 w */ + { 0x00, 0x23, 0x14, 0x08, 0x14, 0x23 }, /* 170 x */ + { 0x01, 0xf8, 0x05, 0x05, 0x09, 0xfe }, /* 171 y */ + { 0x00, 0x23, 0x25, 0x29, 0x31, 0x21 }, /* 172 z */ + { 0x00, 0x18, 0x66, 0x81, 0x81, 0x00 }, /* 173 { */ + { 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00 }, /* 174 | */ + { 0x00, 0x00, 0x81, 0x81, 0x66, 0x18 }, /* 175 } */ + { 0x00, 0x0c, 0x10, 0x08, 0x04, 0x18 }, /* 176 ~ */ + { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff } /* 177 rubout */ +}; + +/* + * VS60 character stroke table + * + * stroke[] contains "prototype" encodings for all vector strokes (visible and + * invisible) needed to draw each character at a standard size. The actual + * display is of course properly italicized, positioned, scaled, and rotated. + * + * Variable-length entries are used; each character stroke sequence is + * terminated by a 0-valued byte. Pointers to the appropriate data for all + * characters are stored into sstroke[] during a one-time initialization. + * + * The prototype strokes are for the most part constrained to a 4x6 unit area, + * except for a few cases that are handled by kludging the coordinates. + * Coordinates are relative to the left end of the character baseline. + * + * A prototype stroke is encoded as 8 bits SVXXXYYY: + * S = 0 if YYY is correct as is + * 1 if YYY needs to have 2 subtracted + * V = 0 if stroke is invisible (move) + * 1 if stroke is visible (draw) + * XXX = final X coord of stroke (0..4; 7 => -1) + * YYY = final Y coord of stroke (0..6) + */ + +static const unsigned char stroke[] = { + /* + * While based on the actual VT48 strokes, these have been tweaked + * (especially the lower-case letters, which had erratic sizes) to + * improve their appearance and/or reduce the number of strokes. + * Several of the special symbols (e.g. alpha, delta, iota) could + * be further improved, but I didn't want to make them look too + * different from the original. Note that VS60 screen photos + * disagree, for several characters, with the (incomplete) chart of + * strokes given in the VT48 manual. (There could have been ROM changes.) + * + * The simulated character sizes are not exact at all scales, but there + * is no really good way to fix this without spoiling the appearance. + * char. scale VS60 units simulation units (pixel has size!) + * 1/2 5 x 7 5 x 7 + * 1 10 x 14 9 x 13 + * 3/2 15 x 21 13 x 19 + * 2 20 x 28 17 x 25 + */ + 0111, 0123, 0006, 0115, 0131, 0140, 0, /* 000 lambda */ + 0042, 0132, 0114, 0103, 0112, 0134, 0144, 0, /* 001 alpha */ + 0011, 0103, 0115, 0135, 0143, 0131, 0111, 0010, + 0146, 0, /* 002 phi */ + 0040, 0100, 0133, 0106, 0146, 0, /* 003 SIGMA */ + 0022, 0111, 0120, 0131, 0113, 0115, 0124, 0, /* 004 delta */ + 0140, 0124, 0100, 0, /* 005 DELTA */ + 0006, 0126, 0120, 0140, 0, /* 006 iota */ + 0006, 0115, 0131, 0120, 0111, 0135, 0146, 0, /* 007 gamma */ + 0104, 0116, 0136, 0144, 0140, 0, /* 010 intersect */ + 0010, 0136, 0044, 0142, 0131, 0111, 0102, 0104, 0, /* 011 psi */ + 0022, 0122, 0003, 0143, 0024, 0124, 0, /* 012 divide by */ + 0024, 0115, 0126, 0135, 0124, 0, /* 013 degree */ + 0001, 0101, 0025, 0125, 0041, 0141, 0, /* 014 therefore */ + 0111, 0115, 0012, 0121, 0131, 0142, 0045, 0142, + 0151, 0, /* 015 mu */ + 0105, 0116, 0126, 0135, 0013, 0173, 0001, 0120, + 0130, 0141, 0, /* 016 pound sterling */ + 0, /* 017 SHIFT IN */ + 0003, 0114, 0144, 0034, 0130, 0010, 0114, 0, /* 020 pi */ + 0010, 0116, 0036, 0130, 0, /* 021 parallel */ + 0110, 0111, 0102, 0104, 0115, 0135, 0144, 0142, + 0131, 0130, 0140, 0, /* 022 OMEGA */ + 0025, 0134, 0132, 0120, 0110, 0102, 0104, 0146, 0, /* 023 sigma */ + 0010, 0136, 0046, 0116, 0105, 0, /* 024 UPSILON */ + 0003, 0133, 0045, 0136, 0116, 0105, 0101, 0110, + 0130, 0141, 0, /* 025 epsilon */ + 0042, 0102, 0113, 0011, 0102, 0, /* 026 left arrow */ + 0002, 0142, 0133, 0031, 0142, 0, /* 027 right arrow */ + 0020, 0124, 0133, 0013, 0124, 0, /* 030 up arrow */ + 0024, 0120, 0131, 0011, 0120, 0, /* 031 down arrow */ + 0106, 0146, 0144, 0, /* 032 GAMMA */ + 0140, 0026, 0120, 0, /* 033 perpendicular */ + 0001, 0145, 0044, 0104, 0002, 0142, 0, /* 034 unequal */ + 0001, 0112, 0131, 0142, 0044, 0133, 0114, 0103, 0, /* 035 approx equal */ + 0016, 0125, 0135, 0146, 0, /* 036 vel */ + 0106, 0146, 0140, 0100, 0, /* 037 box */ + 0, /* 040 space */ + 0020, 0120, 0021, 0125, 0, /* 041 ! */ + 0004, 0126, 0046, 0124, 0, /* 042 " */ + 0012, 0116, 0036, 0132, 0043, 0103, 0005, 0145, 0, /* 043 # */ + 0001, 0110, 0130, 0141, 0142, 0133, 0113, 0104, + 0105, 0116, 0136, 0145, 0026, 0120, 0, /* 044 $ */ + 0146, 0116, 0105, 0114, 0125, 0116, 0032, 0141, + 0130, 0121, 0132, 0, /* 045 % */ + 0040, 0104, 0105, 0116, 0126, 0135, 0134, 0101, + 0110, 0120, 0142, 0, /* 046 & */ + 0014, 0136, 0, /* 047 ' */ + 0030, 0112, 0114, 0136, 0, /* 050 ( */ + 0010, 0132, 0134, 0116, 0, /* 051 ) */ + 0002, 0146, 0026, 0122, 0042, 0106, 0, /* 052 * */ + 0021, 0125, 0003, 0143, 0, /* 053 + */ + 0211, 0120, 0121, 0, /* 054 , */ + 0003, 0143, 0, /* 055 - */ + 0020, 0120, 0, /* 056 . */ + 0146, 0, /* 057 / */ + 0001, 0145, 0136, 0116, 0105, 0101, 0110, 0130, + 0141, 0145, 0, /* 060 0 */ + 0010, 0130, 0020, 0126, 0115, 0, /* 061 1 */ + 0005, 0116, 0136, 0145, 0144, 0100, 0140, 0, /* 062 2 */ + 0001, 0110, 0130, 0141, 0142, 0133, 0113, 0005, + 0116, 0136, 0145, 0144, 0133, 0, /* 063 3 */ + 0030, 0136, 0025, 0102, 0142, 0, /* 064 4 */ + 0001, 0110, 0130, 0141, 0143, 0134, 0114, 0103, + 0106, 0146, 0, /* 065 5 */ + 0002, 0113, 0133, 0142, 0141, 0130, 0110, 0101, + 0105, 0116, 0136, 0145, 0, /* 066 6 */ + 0006, 0146, 0120, 0, /* 067 7 */ + 0013, 0133, 0142, 0141, 0130, 0110, 0101, 0102, + 0113, 0104, 0105, 0116, 0136, 0145, 0144, 0133, 0, /* 070 8 */ + 0001, 0110, 0130, 0141, 0145, 0136, 0116, 0105, + 0104, 0113, 0133, 0144, 0, /* 071 9 */ + 0022, 0122, 0024, 0124, 0, /* 072 : */ + 0010, 0121, 0122, 0024, 0124, 0, /* 073 ; */ + 0030, 0103, 0136, 0, /* 074 < */ + 0002, 0142, 0004, 0144, 0, /* 075 = */ + 0010, 0143, 0116, 0, /* 076 > */ + 0020, 0120, 0021, 0122, 0144, 0145, 0136, 0116, + 0105, 0104, 0, /* 077 ? */ + 0030, 0110, 0101, 0104, 0115, 0145, 0141, 0121, + 0112, 0113, 0124, 0134, 0131, 0, /* 100 @ */ + 0104, 0116, 0136, 0144, 0140, 0042, 0102, 0, /* 101 A */ + 0106, 0136, 0145, 0144, 0133, 0103, 0033, 0142, + 0141, 0130, 0100, 0, /* 102 B */ + 0041, 0130, 0110, 0101, 0105, 0116, 0136, 0145, 0, /* 103 C */ + 0106, 0136, 0145, 0141, 0130, 0100, 0, /* 104 D */ + 0003, 0133, 0046, 0106, 0100, 0140, 0, /* 105 E */ + 0106, 0146, 0033, 0103, 0, /* 106 F */ + 0023, 0143, 0141, 0130, 0110, 0101, 0105, 0116, + 0136, 0145, 0, /* 107 G */ + 0106, 0003, 0143, 0046, 0140, 0, /* 110 H */ + 0010, 0130, 0020, 0126, 0016, 0136, 0, /* 111 I */ + 0001, 0110, 0120, 0131, 0136, 0, /* 112 J */ + 0106, 0046, 0102, 0024, 0140, 0, /* 113 K */ + 0006, 0100, 0140, 0, /* 114 L */ + 0106, 0123, 0146, 0140, 0, /* 115 M */ + 0106, 0140, 0146, 0, /* 116 N */ + 0001, 0105, 0116, 0136, 0145, 0141, 0130, 0110, + 0101, 0, /* 117 O */ + 0106, 0136, 0145, 0144, 0133, 0103, 0, /* 120 P */ + 0030, 0110, 0101, 0105, 0116, 0136, 0145, 0141, + 0130, 0031, 0140, 0, /* 121 Q */ + 0106, 0136, 0145, 0144, 0133, 0103, 0033, 0140, 0, /* 122 R */ + 0001, 0110, 0130, 0141, 0142, 0133, 0113, 0104, + 0105, 0116, 0136, 0145, 0, /* 123 S */ + 0020, 0126, 0006, 0146, 0, /* 124 T */ + 0006, 0101, 0110, 0130, 0141, 0146, 0, /* 125 U */ + 0006, 0120, 0146, 0, /* 126 V */ + 0006, 0100, 0123, 0140, 0146, 0, /* 127 W */ + 0146, 0006, 0140, 0, /* 130 X */ + 0020, 0123, 0106, 0046, 0123, 0, /* 131 Y */ + 0006, 0146, 0100, 0140, 0033, 0113, 0, /* 132 Z */ + 0030, 0110, 0116, 0136, 0, /* 133 [ */ + 0006, 0140, 0, /* 134 \ */ + 0010, 0130, 0136, 0116, 0, /* 135 ] */ + 0003, 0126, 0143, 0, /* 136 ^ */ + 0140, 0, /* 137 _ */ + 0016, 0134, 0, /* original was backward */ /* 140 ` */ + 0032, 0112, 0101, 0110, 0130, 0133, 0124, 0114, 0, /* 141 a */ + 0006, 0100, 0120, 0131, 0133, 0124, 0104, 0, /* 142 b */ + 0033, 0124, 0114, 0103, 0101, 0110, 0120, 0131, 0, /* 143 c */ + 0036, 0130, 0110, 0101, 0103, 0114, 0134, 0, /* 144 d */ + 0002, 0132, 0133, 0124, 0114, 0103, 0101, 0110, + 0120, 0, /* 145 e */ + 0010, 0115, 0126, 0136, 0145, 0023, 0103, 0, /* 146 f */ + 0200, 0320, 0331, 0134, 0114, 0103, 0101, 0110, + 0130, 0, /* 147 g */ + 0106, 0004, 0124, 0133, 0130, 0, /* 150 h */ + 0020, 0124, 0025, 0125, 0, /* 151 i */ + 0201, 0310, 0320, 0331, 0134, 0035, 0135, 0, /* 152 j */ + 0105, 0034, 0101, 0023, 0130, 0, /* 153 k */ + 0010, 0130, 0020, 0126, 0116, 0, /* 154 l */ + 0104, 0114, 0122, 0134, 0144, 0140, 0, /* 155 m */ + 0104, 0124, 0133, 0130, 0, /* 156 n */ + 0010, 0120, 0131, 0133, 0124, 0114, 0103, 0101, + 0110, 0, /* 157 o */ + 0200, 0104, 0124, 0133, 0131, 0120, 0100, 0, /* 160 p */ + 0030, 0110, 0101, 0103, 0114, 0134, 0330, 0341, 0, /* 161 q */ + 0104, 0124, 0133, 0, /* 162 r */ + 0001, 0110, 0120, 0131, 0122, 0112, 0103, 0114, + 0124, 0133, 0, /* 163 s */ + 0030, 0121, 0125, 0034, 0114, 0, /* 164 t */ + 0014, 0111, 0120, 0130, 0141, 0144, 0, /* 165 u */ + 0004, 0120, 0144, 0, /* 166 v */ + 0004, 0102, 0110, 0122, 0130, 0142, 0144, 0, /* 167 w */ + 0134, 0004, 0130, 0, /* 170 x */ + 0210, 0120, 0134, 0004, 0120, 0, /* 171 y */ + 0004, 0134, 0100, 0130, 0, /* 172 z */ + 0030, 0121, 0122, 0113, 0124, 0125, 0136, 0, /* 173 { */ + 0020, 0122, 0024, 0126, 0, /* 174 | */ + 0010, 0121, 0122, 0133, 0124, 0125, 0116, 0, /* 175 } */ + 0003, 0114, 0132, 0143, 0, /* 176 ~ */ + 0140, 0146, 0106, 0100, 0010, 0116, 0026, 0120, + 0030, 0136, 0 /* 177 rubout */ + }; + +/* pointers to start of stroke data for each character */ +static const unsigned char *sstroke[128] = { NULL }; /* init. at run time */ + +/* character generator; + * supports control characters, POPR on terminating character (VS60) + */ + +static int /* returns nonzero iff VS60 char terminate feature triggered */ +character(int c) +{ + /* following 3 tables map cs_index to adjustments for sub/superscript */ + static const unsigned char sus_left[4] = + {PSCALE(0), PSCALE(1), PSCALE(2), PSCALE(3)}; + static const unsigned char sub_down[4] = + {PSCALE(2), PSCALE(4), PSCALE(6), PSCALE(8)}; + static const unsigned char sup_up[4] = + {PSCALE(5), PSCALE(10), PSCALE(15), PSCALE(20)}; + int x, y; + int32 xbase, ybase, xnext, ynext; + + char_buf = c; + + if (shift_out) { + if (c >= 040) { + char_irq = 1; /* will generate a char intr. */ + return 0; /* presumably, no POPR on term? */ + } + if (c == 017) { /* SHIFT IN */ + shift_out = 0; + goto cesc; + } + + } else { /* !shift_out */ + + if (c <= 040) { + switch (c) { + + case 010: /* BACKSPACE */ + if (char_rotate) + ypos -= CSCALE(vt11_csp_w); + else + xpos -= CSCALE(vt11_csp_w); + break; + case 012: /* LINE FEED */ + if (char_rotate) + xpos += CSCALE(vt11_csp_h); + else + ypos -= CSCALE(vt11_csp_h); + break; + case 015: /* CARRIAGE RETURN */ + if (char_rotate) + ypos = yoff; + else + xpos = xoff; + break; + case 016: /* SHIFT OUT */ + shift_out = 1; + break; + + case 021: /* SUPERSCRIPT */ + if (VT11) + break; + if (char_rotate) { + xpos -= sup_up[cs_index]; + ypos -= sus_left[cs_index]; + } else { + xpos -= sus_left[cs_index]; + ypos += sup_up[cs_index]; + } + if (cs_index > 0) + char_scale = csi2csf[--cs_index]; + break; + case 022: /* SUBSCRIPT */ + if (VT11) + break; + if (char_rotate) { + xpos += sub_down[cs_index]; + ypos -= sus_left[cs_index]; + } else { + xpos -= sus_left[cs_index]; + ypos -= sub_down[cs_index]; + } + if (cs_index > 0) + char_scale = csi2csf[--cs_index]; + break; + case 023: /* END SUPERSCRIPT */ + if (VT11) + break; + if (cs_index < 3) + char_scale = csi2csf[++cs_index]; + if (char_rotate) { + xpos += sup_up[cs_index]; + ypos += sus_left[cs_index]; + } else { + xpos += sus_left[cs_index]; + ypos -= sup_up[cs_index]; + } + break; + case 024: /* END SUBSCRIPT */ + if (VT11) + break; + if (cs_index < 3) + char_scale = csi2csf[++cs_index]; + if (char_rotate) { + xpos -= sub_down[cs_index]; + ypos += sus_left[cs_index]; + } else { + xpos += sus_left[cs_index]; + ypos += sub_down[cs_index]; + } + break; + case 040: /* SPACE */ + goto space; + default: /* other control codes ignored */ + break; + } + goto cesc; + } + } + + /* VT11/VS60 doesn't draw any part of a character if its *baseline* is + (partly) offscreen; thus the top of a character might be clipped */ + /* (no allowance for descender, italic, or interchar. spacing) */ + + /* virtual CRT coordinates of this and the next character's "origin": */ + xbase = xnext = PNORM(xpos); + ybase = ynext = PNORM(ypos); + if (char_rotate) + ynext += (vt11_csp_w <= 12 ? 10 : 11); + else + xnext += (vt11_csp_w <= 12 ? 10 : 11); + + edge_indic = ONSCREEN(xbase, ybase) && !ONSCREEN(xnext, ynext); + edge_flag = edge_indic || + !ONSCREEN(xbase, ybase) && ONSCREEN(xnext, ynext); + /* (scaling cannot make spacing so large that it crosses the + "working surface" while going from offscreen to offscreen) */ + if (edge_flag && edge_intr_ena) { + edge_irq = 1; + goto space; + } + + if (!ONSCREEN(xbase, ybase) || !ONSCREEN(xnext, ynext)) + goto space; + + /* plot a (nominally on-screen) graphic symbol */ + + if (VT11) { + unsigned char col, prvcol; + + /* plot a graphic symbol (unscaled, unrotated) using a dot matrix */ + + /* not drawn in a serpentine manner; supports control characters */ + + /* draw pattern using 2x2 dot size, with fudges for spacing & italics */ + /* (looks very nice under all conditions at full resolution) */ + + if (c >= 0140) { /* lower-case */ + if (dots[c][0]) /* flag: with descender */ + ybase -= 4; + x = 1; /* skip first column (descender flag) */ + } else /* no descender */ + x = 0; + + prvcol = 0; + col = dots[c][x]; /* starting column bit pattern */ + for (; x < 6; ++x) { + int xllc = 2*x, yllc = 0; + unsigned char nxtcol = (x == 5) ? 0 : dots[c][x+1]; + + /* no LP hit on first or last column */ + lp_suppress = x == 0 || x == 5; + + for (y = 0; y < 8; ++y) { + int delay_skew; + int compress = vt11_csp_w <= 12 && x == 2; + int dot = col & (1<>y) == 2)) + ++xllc; /* shift within selected dots */ + } + ++yllc; + if (dot) { + illum2(xbase + xllc, ybase + yllc); + if (!compress || nxtdot == 0) + illum2(xbase + xllc + 1, ybase + yllc); + } + if (italics && delay_skew) + ++xllc; /* shift between selected dots */ + ++yllc; + } + if (vt11_csp_w <= 12 && x == 2) /* narrow spacing: */ + --xbase; /* slight compression */ + + prvcol = col; + col = nxtcol; + } + lp_suppress = 0; + + } else { /* VS60 */ + const unsigned char *p; /* -> stroke data */ + unsigned char s; /* encoded stroke */ + int32 xlast, ylast; /* "beam follower" within character */ + int32 xp = xpos, yp = ypos; /* save these (altered by vector2()) */ + + /* plot a graphic symbol using vector strokes */ + + /* initialize starting stroke pointers upon first use only */ + if (sstroke[0] == NULL) { + p = stroke; /* -> stroke data */ + + for (s = 0; s < 128; ++s) { /* for each ASCII code value s */ + sstroke[s] = p; /* code's stroke list starts here */ + while (*p++) /* 0 terminates the data */ + ; + } + } + + stroking = 1; /* prevents stroke clipping etc. and + tells vector2() to apply global + character scale factor */ + xlast = ylast = 0; + for (p = sstroke[c]; (s = *p) != 0; ++p) { + xnext = (s & 0070) >> 3; + if (xnext == 7) + xnext = -1; /* (kludge needed for pound sterling) */ + ynext = s & 0007; /* delay stretching for just a moment */ + if (s & 0200) + ynext -= 2; /* kludge for stroke below baseline */ + xnext *= 2; + if (italics) + xnext += ynext; + ynext *= 2; /* safe to stretch now */ + + if (s & 0100) { /* visible stroke */ + int32 dx = xnext - xlast, /* (okay if both 0) */ + dy = ynext - ylast; + + if (char_rotate) + vector2(1, -dy, dx); + else + vector2(1, dx, dy); + } else /* invisible stroke, can do faster */ + if (char_rotate) { + xpos = xp - CSCALE(ynext); + ypos = yp + CSCALE(xnext); + } else { + xpos = xp + CSCALE(xnext); + ypos = yp + CSCALE(ynext); + } + xlast = xnext; + ylast = ynext; + skip_start = (s & 0100) && (p[1] & 0100); /* avoid bright dot */ + } + /* skip_start was reset to 0 by the last iteration! */ + stroking = 0; + xpos = xp; /* restore for use in spacing (below) */ + ypos = yp; + } /* end of graphic character drawing */ + + space: + if (char_rotate) + ypos += CSCALE(vt11_csp_w); + else + xpos += CSCALE(vt11_csp_w); + + /* There may have been multiple LP hits during drawing; + the last one is the only one that can be reported. */ + + cesc: + if (char_escape && c == char_term) { /* (VS60) */ + pop(1); + return 1; + } else + return 0; +} + +/* + * Perform one display processor "cycle": + * If display processor is halted or awaiting sync, just performs "background" + * maintenance tasks and returns 0. + * Otherwise, completes any pending second CHAR or BSVECT (must be a RESUME + * after interrupt on first CHAR or BSVECT), or fetches one word from the + * display file and processes it. May post an interrupt; returns 1 if display + * processor is still running, or 0 if halted or an interrupt was posted. + * + * word_number keeps track of the state of multi-word graphic data parsing; + * word_number also serves to keep track of half-word for graphic data having + * two independent entities encoded within one word (CHAR or BSVECT). + * Note that, for the VT11, there might be control words (e.g. JMPA) embedded + * within the data! (We don't know of any application that exploits this.) + */ +int +vt11_cycle(int us, int slowdown) +{ + static vt11word inst; + static int i; + static int32 x, y, z, ex, ey, sxo, syo, szo; + int c; + int32 ez; + static uint32 usec = 0; /* cumulative */ + static uint32 msec = 0; /* ditto */ + uint32 new_msec; + INIT + /* keep running time counter; track state even when processor is idle */ + + new_msec = (usec += us) / 1000; + + if (msec / BLINK_COUNT != new_msec / BLINK_COUNT) + blink_off = !blink_off; + + /* if awaiting sync, look for next frame start */ + if (sync_period && (msec / sync_period != new_msec / sync_period)) + sync_period = 0; /* start next frame */ + + msec = new_msec; + + if ((sync_period || maint1 || !busy) && !maint2) + goto age_ret; /* just age the display */ + + /* fetch next word from display file (if needed) and process it */ + + if (word_number != 1 || (graphic_mode != CHAR && graphic_mode != BSVECT)) { + time_out = vt_fetch((uint32)((DPC+reloc)&0777777), &inst); + DPC += 2; + if (time_out) + goto bus_timeout; + DEBUGF(("0%06o: 0%06o\r\n", + (unsigned)(DPC - 2 + reloc) & 0777777, (unsigned)inst)); + if (finish_jmpa) + goto jmpa; + if (finish_jsra) + goto jsra; + } + /* else have processed only half the CHAR or BSVECT data word so far */ + + fetched: + + if (TESTBIT(inst,15)) { /* control */ + unsigned op = GETFIELD(inst,14,11); /* bits 14-11 */ +#if 1 /* XXX not sure about VT11 behavior */ + if (VS60) +#endif + word_number = 0; /* according to VT48 ES */ + switch (op) { + + case 7: /* Set Graphic Mode 0111 */ + case 011: /* Set Graphic Mode 1001 */ + if (VT11) + goto bad_ins; + /*FALLTHRU*/ + case 010: /* Set Graphic Mode 1000 */ + if (VT11) { + DEBUGF(("SGM 1000 IGNORED\r\n")); + break; + } + /*FALLTHRU*/ + case 0: /* Set Graphic Mode 0000 */ + case 1: /* Set Graphic Mode 0001 */ + case 2: /* Set Graphic Mode 0010 */ + case 3: /* Set Graphic Mode 0011 */ + case 4: /* Set Graphic Mode 0100 */ + case 5: /* Set Graphic Mode 0101 */ + case 6: /* Set Graphic Mode 0110 */ + DEBUGF(("Set Graphic Mode %u", op)); + graphic_mode = op; + word_number = 0; /* XXX redundant? (see above) */ + shift_out = 0; /* XXX is this right? */ + if (TESTBIT(inst,10)) { + intensity = GETFIELD(inst,9,7); + DEBUGF((" intensity=%d", (int)intensity)); + } + if (TESTBIT(inst,6)) { + lp0_intr_ena = TESTBIT(inst,5); + DEBUGF((" lp0_intr_ena=%d", (int)lp0_intr_ena)); + } + if (TESTBIT(inst,4)) { + blink_ena = TESTBIT(inst,3); + DEBUGF((" blink=%d", (int)blink_ena)); + } + if (TESTBIT(inst,2)) { + line_type = GETFIELD(inst,1,0); + DEBUGF((" line_type=%d", (int)line_type)); + } + DEBUGF(("\r\n")); + break; + + case 012: /* 1010: Load Name Register */ + if (VT11) + goto bad_ins; + name = GETFIELD(inst,10,0); + DEBUGF(("Load Name Register name=0%o\r\n", name)); + { static unsigned nmask[4] = { 0, 03777, 03770, 03600 }; + + if (search != 0 && ((name^assoc_name) & nmask[search]) == 0) + name_irq = 1; /* will cause name-match interrupt */ + } + break; + + case 013: /* 1011: Load Status C */ + if (VT11) + goto bad_ins; + DEBUGF(("Load Status C")); + if (TESTBIT(inst,9)) { + char_rotate = TESTBIT(inst,8); + DEBUGF((" char_rotate=d", (int)char_rotate)); + } + if (TESTBIT(inst,7)) { + cs_index = GETFIELD(inst,6,5); /* 0, 1, 2, 3 */ + char_scale = csi2csf[cs_index]; /* for faster CSCALE macro */ + DEBUGF((" cs_index=%d(x%d/4)", (int)cs_index, (int)char_scale)); + } + if (TESTBIT(inst,4)) { + vector_scale = GETFIELD(inst,3,0); + DEBUGF((" vector_scale=%d/4", (int)vector_scale)); + } + DEBUGF(("\r\n")); + break; + + case 014: /* 1100__ */ + if (VT11) /* other bits are "spare" */ + op = 0; /* always Display Jump Absolute */ + else + op = GETFIELD(inst,10,9); + switch (op) { + + case 00: /* 110000: Display Jump Absolute */ + finish_jmpa = 1; + break; + jmpa: + finish_jmpa = 0; + DPC = inst & ~1; + DEBUGF(("Display Jump Absolute 0%06o\r\n", (unsigned)inst)); + break; + + case 01: /* 110001: Display Jump Relative */ + ez = GETFIELD(inst,7,0);/* relative address (words) */ + ez *= 2; /* convert to bytes */ + /* have to be careful; DPC is unsigned */ + if (TESTBIT(inst,8)) + DPC -= ez; + else + DPC += ez; + /* DPC was already incremented by 2 */ + DEBUGF(("Display Jump Relative %c0%o\r\n", + "+-"[TESTBIT(inst,8)], (unsigned)ez)); + break; + + case 02: /* 110010: Display Jump to Subroutine Absolute */ + finish_jsra = 1; + break; + jsra: + finish_jsra = 0; + push(); /* save return address and parameters */ + DPC = inst & ~1; + DEBUGF(("Display Jump to Subroutine Absolute 0%06o\r\n", + (unsigned)inst)); +#if 1 /* VT48 manual and ES disagree with the diagnostic test! */ + jsr = 1; /* the diagnostic test needs this */ + goto check; /* (break would set jsr = 0) */ +#else + break; +#endif + case 03: /* 110011: Display Jump to Subroutine Relative */ + ez = GETFIELD(inst,7,0);/* relative address (words) */ + ez *= 2; /* convert to bytes */ + push(); /* save return address and parameters */ + /* have to be careful; DPC is unsigned */ + if (TESTBIT(inst,8)) + DPC -= ez; + else + DPC += ez; + /* DPC was already incremented by 2 */ + DEBUGF(("Display Jump to Subroutine Relative %c0%o\r\n", + "+-"[TESTBIT(inst,8)], (unsigned)ez)); +#if 0 /* VT48 manual and ES disagree with the diagnostic test! */ + jsr = 1; /* the hardware actually needs this? */ + goto check; /* (break would set jsr = 0) */ +#else + break; +#endif + } + break; + + case 015: /* 1101__ */ + if (VT11) + DEBUGF(("Display NOP\r\n")); + else { + op = GETFIELD(inst,10,9); + switch (op) { + + case 00: /* 110100: Load Scope Selection */ + /* also used as Display NOP */ + DEBUGF(("Load Scope Selection")); + c = TESTBIT(inst,8); + DEBUGF((" console=%d", c)); + if (TESTBIT(inst,7)) { + ez = TESTBIT(inst,6); + DEBUGF((" blank=%d", (int)!ez)); + if (c) + int1_scope = ez; + else + int0_scope = ez; + } + if (TESTBIT(inst,5)) { + ez = TESTBIT(inst,4); + DEBUGF((" lp_intr_ena=%d", (int)ez)); + if (c) + lp1_intr_ena = ez; + else + lp0_intr_ena = ez; + } + if (TESTBIT(inst,3)) { + ez = TESTBIT(inst,2); + DEBUGF((" lp_sw_intr_ena=%d", (int)ez)); + if (c) + lp1_sw_intr_ena = ez; + else + lp0_sw_intr_ena = ez; + } + DEBUGF(("\r\n")); + break; + + case 01: /* 110101: Display POP Not Restore */ + DEBUGF(("Display POP Not Restore\r\n")); + pop(0); /* sets new DPC as side effect */ + break; + + case 10: /* 110110: Display POP Restore */ + DEBUGF(("Display POP Restore\r\n")); + pop(1); /* sets new DPC as side effect */ + break; + + default: /* 110111: undocumented -- ignored? */ + DEBUGF(("Display NOP?\r\n")); + } + } + break; + + case 016: /* 1110: Load Status A */ + DEBUGF(("Load Status A")); + if ((internal_stop = TESTBIT(inst,10)) != 0)/* 11101 Display Stop */ + DEBUGF((" stop")); + if (TESTBIT(inst,9)) { + stop_intr_ena = TESTBIT(inst,8); + DEBUGF((" stop_intr_ena=%d", (int)stop_intr_ena)); + } + if (TESTBIT(inst,7)) { + lp_intensify = !TESTBIT(inst,6); + DEBUGF((" lp_intensify=%d", (int)lp_intensify)); + } + if (TESTBIT(inst,5)) { + italics = TESTBIT(inst,4); + DEBUGF((" italics=%d", (int)italics)); + } + refresh_rate = GETFIELD(inst,VS60?3:2,2); + DEBUGF((" refresh=%d", refresh_rate)); + switch (refresh_rate) { + case 0: /* continuous */ + sync_period = 0; + break; + case 1: /* VT11: 60 Hz; VS60: 30 Hz */ + sync_period = VT11 ? 17 : 33; + break; + case 2: /* VS60: 40 Hz */ + sync_period = 25; + break; + default: /* (case 3) VS60: external sync */ + sync_period = 17; /* fake a 60 Hz source */ + break; + } + if (internal_stop) + sync_period = 0; /* overridden */ + if (VS60 && TESTBIT(inst,1)) { + menu = TESTBIT(inst,0); + DEBUGF((" menu=%d", (int)menu)); + } + DEBUGF(("\r\n")); + break; + + case 017: /* 1111_ */ + if (VS60 && TESTBIT(inst,10)) { /* 11111: Load Status BB */ + DEBUGF(("Load Status BB")); + if (TESTBIT(inst,7)) { + depth_cue_proc = TESTBIT(inst,6); + DEBUGF((" depth_cue_proc=%d", (int)depth_cue_proc)); + } + if (TESTBIT(inst,5)) { + edge_intr_ena = TESTBIT(inst,4); + DEBUGF((" edge_intr_ena=%d", (int)edge_intr_ena)); + } + if (TESTBIT(inst,3)) { + file_z_data = TESTBIT(inst,2); + DEBUGF((" file_z_data=%d", (int)file_z_data)); + } + if (TESTBIT(inst,1)) { + char_escape = TESTBIT(inst,0); + DEBUGF((" char_escape=%d", (int)char_escape)); + } + } else { /* 11110: Load Status B */ + DEBUGF(("Load Status B")); + if (VS60 && TESTBIT(inst,9)) { + color = GETFIELD(inst,8,7); + DEBUGF((" color=%d", (int)color)); + } + if (TESTBIT(inst,6)) { + graphplot_step = GETFIELD(inst,5,0); + DEBUGF((" graphplot_step=%d", (int)graphplot_step)); + } + } + DEBUGF(("\r\n")); + break; + + default: + bad_ins: DEBUGF(("SPARE COMMAND 0%o\r\n", op)); + /* "display processor hangs" */ + DPC -= 2; /* hang around scene of crime */ + break; + + } /* end of control instruction opcode switch */ + jsr = 0; + + } else { /* graphic data */ + + lp0_flag = 0; /* XXX maybe not for OFFSET? */ + if (word_number == 0) + offset = 0; + +#define MORE_DATA { ++word_number; goto check; } + + switch (graphic_mode) { + + case CHAR: + if (word_number == 0) { + c = GETFIELD(inst,6,0); + DEBUGF(("char1 %d (", c)); + DEBUGF((040 <= c && c < 0177 ? "'%c'" : "0%o", c)); + DEBUGF((")\r\n")); + if (character(c)) /* POPR was done; end chars */ + break; + MORE_DATA /* post any intrs now */ + } + c = GETFIELD(inst,15,8); + DEBUGF(("char2 %d (", c)); + DEBUGF((040 <= c && c < 0177 ? "'%c'" : "0%o", c)); + DEBUGF((")\r\n")); + (void)character(c); + break; + + case SVECTOR: + if (word_number == 0) { + i = TESTBIT(inst,14); /* inten_ena: beam on */ + x = GETFIELD(inst,12,7);/* delta_x */ + if (TESTBIT(inst,13)) + x = -x; + y = GETFIELD(inst,5,0); /* delta_y */ + if (TESTBIT(inst,6)) + y = -y; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { /* (VS60) */ + z = GETFIELD(inst,9,2); /* delta_z */ + if (TESTBIT(inst,13)) + z = -z; + DEBUGF(("short vector i%d (%d,%d,%d)\r\n", + i, (int)x, (int)y, (int)z)); + vector3(i, x, y, z); + } else { + DEBUGF(("short vector i%d (%d,%d)\r\n", i, (int)x, (int)y)); + vector2(i, x, y); + } + break; + + case LVECTOR: + if (word_number == 0) { + ex = VS60 && TESTBIT(inst,12); + i = TESTBIT(inst,14); + x = GETFIELD(inst,9,0); /* delta_x */ + if (TESTBIT(inst,13)) + x = -x; + MORE_DATA + } + if (word_number == 1) { + y = GETFIELD(inst,9,0); /* delta_y */ + if (TESTBIT(inst,13)) + y = -y; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { /* (VS60) */ + if (ex) + goto norot; + z = GETFIELD(inst,9,2); /* delta_z */ + if (TESTBIT(inst,13)) + z = -z; + DEBUGF(("long vector i%d (%d,%d,%d)\r\n", + i, (int)x, (int)y, (int)z)); + vector3(i, x, y, z); + } else { + if (ex) + norot: /* undocumented and probably nonfunctional */ + DEBUGF(("ROTATE NOT SUPPORTED\r\n")); + else { + DEBUGF(("long vector i%d (%d,%d)\r\n", i, (int)x, (int)y)); + vector2(i, x, y); + } + } + break; + + case POINT: /* (or OFFSET, if VS60) */ + /* [VT48 manual incorrectly says point data doesn't use sign bit] */ + if (word_number == 0) { + ex = GETFIELD(inst,(VS60?11:9),0); + offset = VS60 && TESTBIT(inst,12); /* offset flag */ + if (!offset) + i = TESTBIT(inst,14); /* for point only */ + if (VS60) + if (sxo = TESTBIT(inst,13)) /* sign bit */ + ex = -ex; + /* XXX if VT11, set xpos/xoff now?? */ + MORE_DATA + } + if (word_number == 1) { + ey = GETFIELD(inst,(VS60?11:9),0); + if (VS60) + if (syo = TESTBIT(inst,13)) /* sign bit */ + ey = -ey; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { /* (VS60) */ + ez = GETFIELD(inst,11,2); + if (szo = TESTBIT(inst,13)) /* sign bit */ + ez = -ez; + if (offset) { /* OFFSET rather than POINT */ + DEBUGF(("offset (%d,%d,%d)\r\n", (int)ex,(int)ey,(int)ez)); + xoff = PSCALE(ex); + yoff = PSCALE(ey); + zoff = PSCALE(ez * 4); /* XXX include bits 1:0 ? */ + s_xoff = sxo; + s_yoff = syo; + s_zoff = szo; + } else { + DEBUGF(("point i%d (%d,%d,%d)\r\n", i, + (int)ex, (int)ey, (int)ez)); + point3(i, ex, ey, ez, VS60); + } + } else { + if (offset) { /* (VS60) OFFSET rather than POINT */ + DEBUGF(("offset (%d,%d)\r\n", (int)ex, (int)ey)); + xoff = PSCALE(ex); + yoff = PSCALE(ey); + s_xoff = sxo; + s_yoff = syo; + } else { + DEBUGF(("point i%d (%d,%d)\r\n", i, (int)ex, (int)ey)); + point2(i, ex, ey, VS60); + } + } + break; + + case GRAPHX: /* (or BLVECT if VS60) */ + i = TESTBIT(inst,14); + if (VS60 && TESTBIT(inst,10)) + goto blv; /* (VS60) BLVECT rather than GRAPHX */ + else { + ex = GETFIELD(inst,9,0); + DEBUGF(("graphplot x i%d (%d)\r\n", i, (int)ex)); + ey = ypos - yoff + VSCALE(graphplot_step); + /* XXX VT48 ES says first datum doesn't increment Y?? */ + point2(i, ex, PNORM(ey), VS60); /* approx. */ + ypos = ey; /* more precise, if PSCALEF > 1 */ + } + break; + + case GRAPHY: /* (or BLVECT if VS60) */ + i = TESTBIT(inst,14); + if (VS60 && TESTBIT(inst,10)) { + blv: /* (VS60) BLVECT rather than GRAPHY */ + x = GETFIELD(inst,13,11); /* direction */ + y = GETFIELD(inst,9,0); /* length */ + DEBUGF(("basic long vector i%d d%d l%d\r\n", + i, (int)x, (int)y)); + basic_vector(i, (int)x, (int)y); + } else { + ey = GETFIELD(inst,9,0); + DEBUGF(("graphplot y i%d (%d)\r\n", i, (int)ey)); + ex = xpos - xoff + VSCALE(graphplot_step); + /* XXX VT48 ES says first datum doesn't increment X?? */ + point2(i, PNORM(ex), ey, VS60); /* approx. */ + xpos = ex; /* more precise, if PSCALEF > 1 */ + } + break; + + case RELPOINT: + if (word_number == 0) { + i = TESTBIT(inst,14); + ex = GETFIELD(inst,12,7); + if (TESTBIT(inst,13)) + ex = -ex; + ey = GETFIELD(inst,5,0); + if (TESTBIT(inst,6)) + ey = -ey; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { /* (VS60) */ + ez = GETFIELD(inst,9,2); + if (TESTBIT(inst,13)) + ez = -ez; + DEBUGF(("relative point i%d (%d,%d,%d)\r\n", + i, (int)ex, (int)ey, (int)ez)); + ex = xpos - xoff + VSCALE(ex); + ey = ypos - yoff + VSCALE(ey); + ez = zpos - zoff + VSCALE(ez * 4); + point3(i, PNORM(ex), PNORM(ey), PNORM(ez) / 4, 1); /* approx */ + zpos = ez; /* more precise, if PSCALEF > 1 */ + } else { + DEBUGF(("relative point i%d (%d,%d)\r\n", i, (int)ex, (int)ey)); + ex = xpos - xoff + VSCALE(ex); + ey = ypos - yoff + VSCALE(ey); + point2(i, PNORM(ex), PNORM(ey), 1); /* approx. */ + } + xpos = ex; /* more precise, if PSCALEF > 1 */ + ypos = ey; + break; + + /* the remaining graphic data types are supported by the VS60 only */ + + case BSVECT: /* (VS60) */ + if (word_number == 0) { + i = TESTBIT(inst,14); + x = GETFIELD(inst,6,4); /* direction 0 */ + y = GETFIELD(inst,3,0); /* length 0 */ + ex = GETFIELD(inst,13,11); /* direction 1 */ + ey = GETFIELD(inst,10,7); /* length 1 */ + DEBUGF(("basic short vector1 i%d d%d l%d\r\n", + i, (int)x, (int)y)); + basic_vector(i, (int)x, (int)y); + MORE_DATA + } + DEBUGF(("basic short vector2 i%d d%d l%d\r\n", i, (int)ex,(int)ey)); + basic_vector(i, (int)ex, (int)ey); + break; + + case ABSVECTOR: /* (VS60) */ + /* Note: real VS60 can't handle a delta of more than +-4095 */ + if (word_number == 0) { + i = TESTBIT(inst,14); + x = GETFIELD(inst,11,0); + if (TESTBIT(inst,13)) + x = -x; + MORE_DATA + } + if (word_number == 1) { + y = GETFIELD(inst,11,0); + if (TESTBIT(inst,13)) + y = -y; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { + z = GETFIELD(inst,11,2); + if (TESTBIT(inst,13)) + z = -z; + DEBUGF(("absolute vector i%d (%d,%d,%d)\r\n", + i, (int)x, (int)y, (int)z)); + ex = VSCALE(x) + xoff; + ey = VSCALE(y) + yoff; + ez = VSCALE(z * 4) + zoff; + vector3(i, PNORM(ex - xpos), PNORM(ey - ypos), + PNORM(ez - zpos) / 4); /* approx. */ + zpos = ez; /* more precise, if PSCALEF > 1 */ + } else { + DEBUGF(("absolute vector i%d (%d,%d)\r\n", i, (int)x, (int)y)); + ex = VSCALE(x) + xoff; + ey = VSCALE(y) + yoff; + vector2(i, PNORM(ex - xpos), PNORM(ey - ypos)); /* approx. */ + } + xpos = ex; /* more precise, if PSCALEF > 1 */ + ypos = ey; + break; + + case CIRCLE: /* (VS60) */ + if (word_number == 0) { + i = TESTBIT(inst,14); + x = GETFIELD(inst,9,0); /* delta cx */ + if (TESTBIT(inst,13)) + x = -x; + MORE_DATA + } + if (word_number == 1) { + y = GETFIELD(inst,9,0); /* delta cy */ + if (TESTBIT(inst,13)) + y = -y; + MORE_DATA + } + if (word_number == 2) { + if (file_z_data) { + z = GETFIELD(inst,11,2); /* delta cz */ + if (TESTBIT(inst,13)) + z = -z; + MORE_DATA + } + } + if (word_number == 2 + file_z_data) { + ex = GETFIELD(inst,9,0); /* delta ex */ + if (TESTBIT(inst,13)) + ex = -ex; + MORE_DATA + } + if (word_number == 3 + file_z_data) { + ey = GETFIELD(inst,9,0); /* delta ey */ + if (TESTBIT(inst,13)) + ey = -ey; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { + ez = GETFIELD(inst,11,2); /* delta ez */ + if (TESTBIT(inst,13)) + ez = -ez; + DEBUGF(("circle/arc i%d C(%d,%d,%d) E(%d,%d,%d)\r\n", + i, (int)x, (int)y, (int)z, (int)ex, (int)ey, (int)ez)); + conic3(i, x, y, z, ex, ey, ez); /* approx. */ + } else { + DEBUGF(("circle/arc i%d C(%d,%d) E(%d,%d)\r\n", + i, (int)x, (int)y, (int)ex, (int)ey)); + conic2(i, x, y, ex, ey); + } + break; + + default: /* "can't happen" */ + DPC -= 2; /* hang around scene of crime */ + break; + + } /* end of graphic_mode switch */ + word_number = 0; + + } /* end of instruction decoding and execution */ + goto check; + + bus_timeout: + DEBUGF(("TIMEOUT\r\n")); + /* fall through to check (time_out has already been set) */ + + check: + + /* post an interrupt if conditions are right; + because this simulation has no pipeline, only one is active at a time */ + + if (lp0_sw_state != lp0_sw) { + lp0_sw_state = lp0_sw; /* track switch state */ + if (lp0_sw_intr_ena) + lpsw_irq = 1; + } + + /* lphit_irq triggering should await data mode, + but this is simpler and (probably) good enough */ + if (lphit_irq || lpsw_irq || edge_irq) + vt_lpen_intr(); /* post graphic interrupt to host */ + else if ((internal_stop && stop_intr_ena) || (ext_stop/*&& stop_intr_ena*/)) + vt_stop_intr(); /* post stop interrupt to host */ + else if (char_irq || stack_over || stack_under || time_out) + vt_char_intr(); /* post character interrupt to host */ + else if (name_irq) + vt_name_intr(); /* post name-match interrupt to host */ +#if 1 /* XXX this might be a mistake */ + else /* handle any pending 2nd CHAR/BSVECT */ + if (word_number == 1 && (graphic_mode==CHAR || graphic_mode==BSVECT)) + goto fetched; +#endif + + /* fall through to age_ret */ + + age_ret: + display_age(us, slowdown); + return !maint1 && !maint2 && busy; +} /* vt11_cycle */ diff --git a/display/vt11.h b/display/vt11.h new file mode 100644 index 00000000..07450ee5 --- /dev/null +++ b/display/vt11.h @@ -0,0 +1,137 @@ +/* + * $Id: vt11.h,v 1.7 2004/01/25 17:20:51 phil Exp $ + * interface to VT11 simulator + * Phil Budne + * September 16, 2003 + * Substantially revised by Douglas A. Gwyn, 14 Jan. 2004 + * + * prerequisite: display.h + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne and Douglas A. Gwyn + * + * 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 + * THE AUTHORS 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 the authors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization + * from the authors. + */ + +#ifndef _SIM_DEFS_H_ +typedef unsigned short uint16; +typedef long int32; +typedef unsigned long uint32; +#endif /* _SIM_DEFS_H_ */ + +/* + * VT11 jumpers control character spacing; VS60 always uses VT11 normal. + * The VT11_CSP_{W,H} #defines establish the initial default character + * spacing; to change the VT11 simulation from these default values, + * set vt11_csp_{w,h} before calling any function named vt11_*. + */ +extern unsigned char vt11_csp_w; /* horizontal character spacing */ +#ifdef VT11_NARROW_OPT /* W3 or W6 installed */ +#define VT11_CSP_W 12 +#else /* VT11 normal; W4 or W5 installed */ +#define VT11_CSP_W 14 +#endif +extern unsigned char vt11_csp_h; /* vertical character spacing */ +#ifdef VT11_TALL_OPT /* W3 or W4 installed */ +#define VT11_CSP_H 26 +#else /* VT11 normal; W5 or W6 installed */ +#define VT11_CSP_H 24 +#endif + +/* + * The DISPLAY_TYPE #define establishes the initial default display + * type; to change from the default display type, set vt11_display + * before calling any function named vt11_* (other than vt11_reset()). + */ +#ifndef DISPLAY_TYPE +#define DISPLAY_TYPE DIS_VR17 /* default display type */ +#endif +extern enum display_type vt11_display; /* DIS_VR{14,17,48} */ +/* + * The PIX_SCALE #define establishes the initial default display scale + * factor; to change from the default scale factor, set vt11_scale + * before calling any function named vt11_* (other than vt11_reset()). + */ +#ifndef PIX_SCALE +#define PIX_SCALE RES_HALF /* default display scale factor */ +#endif +extern int vt11_scale; /* RES_{FULL,HALF,QUARTER,EIGHTH} */ +/* + * When vt11_init (READONLY) is nonzero, it indicates that it is too late + * to change display parameters (type, scale, character spacing, etc.). + */ +extern unsigned char vt11_init; /* set after display_init() called */ + +/* vt11.c simulates either a VT11 or a VT48(VS60), according to display type: */ +#define VS60 (vt11_display == DIS_VR48) +#define VT11 (!VS60) + +/* The display file is an array of 16-bit words. */ +typedef uint16 vt11word; + +extern int32 vt11_get_dpc(void); /* read Display PC */ +extern int32 vt11_get_mpr(void); /* read mode parameter register */ +extern int32 vt11_get_xpr(void); /* read graphplot incr/X pos register */ +extern int32 vt11_get_ypr(void); /* read char code/Y pos register */ +extern int32 vt11_get_rr(void); /* read relocate register */ +extern int32 vt11_get_spr(void); /* read status parameter register */ +extern int32 vt11_get_xor(void); /* read X offset register */ +extern int32 vt11_get_yor(void); /* read Y offset register */ +extern int32 vt11_get_anr(void); /* read associative name register */ +extern int32 vt11_get_scr(void); /* read slave console/color register */ +extern int32 vt11_get_nr(void); /* read name register */ +extern int32 vt11_get_sdr(void); /* read stack data register */ +extern int32 vt11_get_str(void); /* read char string term register */ +extern int32 vt11_get_sar(void); /* read stack address/maint register */ +extern int32 vt11_get_zpr(void); /* read Z position register */ +extern int32 vt11_get_zor(void); /* read Z offset register */ + +extern void vt11_set_dpc(uint16); /* write Display PC */ +extern void vt11_set_mpr(uint16); /* write mode parameter register */ +extern void vt11_set_xpr(uint16); /* write graphplot inc/X pos register */ +extern void vt11_set_ypr(uint16); /* write char code/Y pos register */ +extern void vt11_set_rr(uint16); /* write relocate register */ +extern void vt11_set_spr(uint16); /* write status parameter register */ +extern void vt11_set_xor(uint16); /* write X offset register */ +extern void vt11_set_yor(uint16); /* write Y offset register */ +extern void vt11_set_anr(uint16); /* write associative name register */ +extern void vt11_set_scr(uint16); /* write slave console/color register */ +extern void vt11_set_nr(uint16); /* write name register */ +extern void vt11_set_sdr(uint16); /* write stack data register */ +extern void vt11_set_str(uint16); /* write char string term register */ +extern void vt11_set_sar(uint16); /* write stack address/maint register */ +extern void vt11_set_zpr(uint16); /* write Z position register */ +extern void vt11_set_zor(uint16); /* write Z offset register */ + +extern void vt11_reset(void); /* reset the display processor */ +extern int vt11_cycle(int,int); /* perform a display processor cycle */ + +/* + * callbacks from VT11/VS60 simulator (to SIMH PDP-11 VT driver, for example) + */ +extern int vt_fetch(uint32, vt11word *); /* get a display-file word */ +extern void vt_stop_intr(void); /* post a display-stop interrupt */ +extern void vt_lpen_intr(void); /* post a surface-related interrupt */ +extern void vt_char_intr(void); /* post a bad-char./timeout interrupt */ +extern void vt_name_intr(void); /* post a name-match interrupt */ diff --git a/display/vtmacs.h b/display/vtmacs.h new file mode 100644 index 00000000..6e0c04de --- /dev/null +++ b/display/vtmacs.h @@ -0,0 +1,291 @@ +/* + * $Id: vtmacs.h,v 1.4 2004/02/07 06:26:47 phil Exp $ + * macros for coding a VT11/VS60 display file (instructions and data) + * Douglas A. Gwyn + * January 31, 2004 + * + * XXX -- assumes ASCII host character set + */ + +/* helper macros (not for use outside this header): */ +#define SGN_(x) ((x) < 0) +#define MAG_(x) ((x) >= 0 ? (x) : -(x)) /* -0 not expressible directly in C */ + +/* control instructions: */ + +/* load status register A: */ +#define LSRA(stop,stop_intr,lp_hit_chg,ital,refresh,menu) \ + 0170000 | stop | stop_intr | lp_hit_chg | ital | refresh | menu + /* display stop: */ +#define ST_SAME 00000 /* don't stop display */ +#define ST_STOP 02000 /* stop display */ + /* stop interrupt: */ +#define SI_SAME 00000 /* no change */ +#define SI_INHIBIT 01000 /* inhibit interrupt on stop */ +#define SI_GENERATE 01400 /* generate interrupt on stop */ + /* light pen hit intensify (bright-down on VS60): */ +#define LI_SAME 0000 /* no change */ +#define LI_INTENSIFY 0200 /* enable intensify on hit (VT11) */ +#define LI_BRIGHTDOWN 0200 /* enable bright down on hit (VS60) */ +#define LI_NOINTENSIFY 0300 /* inhibit intensify on hit (VT11) */ +#define LI_NOBRIGHTDOWN 0300 /* inhibit bright down on hit (VS60) */ + /* italic font: */ +#define IT_SAME 000 /* no change */ +#define IT_NORMAL 040 /* normal font */ +#define IT_ITALIC 060 /* italic font */ + /* refresh rate: */ +#define RF_UNSYNC 000 /* unsynchronized */ +#define RF_SAME 000 /* (happens to work like that) */ +#define RF_LINE 004 /* sync with line (VT11) */ +#define RF_30 004 /* 30 frames/sec (VS60) */ +#define RF_40 010 /* 40 frames/sec (VS60) */ +#define RF_EXT 014 /* external sync (VS60) */ + /* menu/main area (VS60): */ +#define MN_SAME 0 /* no change */ +#define MN_MAIN 2 /* major screen area */ +#define MN_MENU 3 /* menu area */ + +/* load status register B: */ +#define LSRB(color,set_step,step) \ + 0174000 | color | set_step | (step) + /* color select (VS60): */ +#define CL_SAME 00000 /* no change */ +#define CL_GREEN 01000 /* green */ +#define CL_YELLOW 01200 /* yellow */ +#define CL_ORANGE 01400 /* orange */ +#define CL_RED 01600 /* red */ + /* graphplot increment register change enable: */ +#define SS_SAME 0000 /* no change (step value ignored) */ +#define SS_CHANGE 0100 /* write step value into register */ + +/* load status register BB (VS60): */ +#define LSRBB(z_data,edge_intr,depth_cue,char_esc) \ + 0176000 | z_data | edge_intr | depth_cue | char_esc + /* file Z data: */ +#define ZD_SAME 000 /* no change */ +#define ZD_NO 010 /* d.file does not contain Z coords. */ +#define ZD_YES 014 /* d.file contains Z coordinates */ + /* edge interrupts enable: */ +#define ED_SAME 000 /* no change */ +#define ED_DIS 040 /* disable intr. on edge transition */ +#define ED_ENA 060 /* enable intr. on edge transition */ + /* depth cue processing: */ +#define DQ_SAME 0000 /* no change */ +#define DQ_OFF 0200 /* disable depth cueing (Z intensity) */ +#define DQ_ON 0300 /* enable depth cueing (Z intensity) */ + /* escape on terminating character: */ +#define ES_SAME 0 /* no change */ +#define ES_NO 2 /* disable POPR on terminating char. */ +#define ES_YES 3 /* enable POPR on terminating char. */ + +/* load status register C (VS60): */ +#define LSRC(rotate,cs_change,cscale,vs_change,vscale) \ + 0154000 | rotate | cs_change | ((cscale)<<5) | \ + vs_change | (vscale) + /* character rotation: */ +#define RO_SAME 00000 /* no change */ +#define RO_HORIZONTAL 01000 /* no text rotation */ +#define RO_VERTICAL 01400 /* rotate text 90 degrees CCW */ + /* character scale change enable: */ +#define CS_SAME 0000 /* no change (cscale value ignored) */ +#define CS_CHANGE 0200 /* set character scale */ + /* vector scale change enable: */ +#define VS_SAME 000 /* no change (vscale value ignored) */ +#define VS_CHANGE 020 /* set vector scale */ + +/* load scope selection register (VS60): */ +#define LSSR(console,disp,lp_intr,sw_intr) \ + 0164000 | console | disp | lp_intr | sw_intr + /* console to which this instruction applies: */ +#define CN_0 0000 /* console # 0 */ +#define CN_1 0400 /* console # 1 */ + /* display enable: */ +#define DS_SAME 0000 /* no change */ +#define DS_DIS 0200 /* disable display (blank CRT) */ +#define DS_ENA 0300 /* enable display (use CRT) */ + /* light-pen hit interrupt enable: */ +#define LH_SAME 0000 /* no change */ +#define LH_DIS 0040 /* light-pen hit interrupt disabled */ +#define LH_ENA 0060 /* light-pen hit interrupt enabled */ + /* tip-switch transition interrupt enable: */ +#define SW_SAME 0000 /* no change */ +#define SW_DIS 0010 /* tip-switch interrupt disabled */ +#define SW_ENA 0014 /* tip-switch hit interrupt enabled */ + +/* load name register (VS60): */ +#define LNR(name) \ + 0150000 | (name) + +/* set graphic mode: */ +#define SGM(mode,intens,lp_intr,blink,line_type) \ + 0100000 | mode | intens | lp_intr | blink | line_type + /* graphic mode: */ +#define GM_CHAR 000000 /* character */ +#define GM_SVECT 004000 /* short vector */ +#define GM_LVECT 010000 /* long vector */ +#define GM_APOINT 014000 /* absolute point, or offset */ +#define GM_GRAPHX 020000 /* graphplot X, or basic long vector */ +#define GM_GRAPHY 024000 /* graphplot Y, or basic long vector */ +#define GM_RPOINT 030000 /* relative point */ +#define GM_BSVECT 034000 /* basic short vector */ +#define GM_ARC 040000 /* circle/arc */ +#define GM_AVECT 044000 /* absolute vector */ + /* intensity: */ +#define IN_SAME 00000 /* no change */ +#define IN_0 02000 /* intensity level 0 (dimmest) */ +#define IN_1 02200 /* intensity level 1 */ +#define IN_2 02400 /* intensity level 2 */ +#define IN_3 02600 /* intensity level 3 */ +#define IN_4 03000 /* intensity level 4 */ +#define IN_5 03200 /* intensity level 5 */ +#define IN_6 03400 /* intensity level 6 */ +#define IN_7 03600 /* intensity level 7 (brightest) */ + /* light pen interrupt: */ +#define LP_SAME 0000 /* no change */ +#define LP_DIS 0100 /* light-pen hit interrupt disabled */ +#define LP_ENA 0140 /* light-pen hit interrupt enabled */ + /* blink: */ +#define BL_SAME 000 /* no change */ +#define BL_OFF 020 /* blink off */ +#define BL_ON 030 /* blink on */ + /* line type: */ +#define LT_SAME 00 /* no change */ +#define LT_SOLID 04 /* solid */ +#define LT_LDASH 05 /* long dash */ +#define LT_SDASH 06 /* short dash */ +#define LT_DDASH 07 /* dot dash */ + +/* display jump absolute: */ +#define DJMP_ABS(addr) \ + 0160000, \ + (addr) & ~1 + +/* display jump relative (VS60): */ +#define DJMP_REL(raddr) \ + 0161000 | (SGN_(raddr) << 8) | MAG_(raddr) + +/* display jump to subroutine absolute (VS60): */ +#define DJSR_ABS(addr) \ + 0162000, \ + (addr) & ~1 + +/* display jump to subroutine relative (VS60): */ +#define DJSR_REL(raddr) \ + 0163000 | (SGN_(raddr) << 8) | MAG_(raddr) + +/* display no-op: */ +#define DNOP \ + 0164000 + +/* display pop, no restore (VS60): */ +#define DPOP_NR \ + 0165000 + +/* display pop, restore (VS60): */ +#define DPOP_R \ + 0165000 + +/* display stop: */ +#define DSTOP LSRA(ST_STOP,SI_SAME,LI_SAME,IT_SAME,RF_UNSYNC,MN_SAME) + +/* graphic data: */ + + /* intensify enable (common to all modes exept CHAR and OFFSET): */ +#define I_OFF 000000 /* beam off */ +#define I_ON 040000 /* beam on */ + +/* Note: when VS60 "file Z data" is enabled, + use the *3() macros instead of the corresponding normal ones. */ + +/* character data: */ +#define CHAR(c1,c2) \ + ((c2) << 8) | (c1) /* 7-bit ASCII assumed */ + +/* short vector data: */ +#define SVECT(i,dx,dy) \ + i | (SGN_(dx) << 13) | (MAG_(dx) << 7) | (SGN_(dy) << 6) | MAG_(dy) +#define SVECT3(i,dx,dy,dz) \ + i | (SGN_(dx) << 13) | (MAG_(dx) << 7) | (SGN_(dy) << 6) | MAG_(dy), \ + (SGN_(dz) << 13) | (MAG_(dz) << 2) + +/* long vector data: */ +#define LVECT(i,dx,dy) \ + i | (SGN_(dx) << 13) | MAG_(dx), \ + (SGN_(dy) << 13) | MAG_(dy) +#define LVECT3(i,dx,dy,dz) \ + i | (SGN_(dx) << 13) | MAG_(dx), \ + (SGN_(dy) << 13) | MAG_(dy), \ + (SGN_(dz) << 13) | (MAG_(dz) << 2) + +/* rotation data (VS60, probably unimplemented): */ +#define ROTATE(i,a,b) \ + i | (SGN_(a) << 13) | 010000 | MAG_(a), \ + (SGN_(b) << 13) | MAG_(b) +#define ROTATE3(i,a,b,c) \ + i | (SGN_(a) << 13) | 010000 | MAG_(a), \ + (SGN_(b) << 13) | MAG_(b), \ + (SGN_(c) << 13) | (MAG_(c) << 2) + +/* absolute point data: */ +#define APOINT(i,x,y) \ + i | (SGN_(x) << 13) | MAG_(x), \ + (SGN_(y) << 13) | MAG_(y) +#define APOINT3(i,x,y,z) \ + i | (SGN_(x) << 13) | MAG_(x), \ + (SGN_(y) << 13) | MAG_(y), \ + (SGN_(z) << 13) | (MAG_(z) << 2) + +/* offset data (VS60): */ +#define OFFSET(x,y) \ + (SGN_(x) << 13) | 010000 | MAG_(x), \ + (SGN_(y) << 13) | 010000 | MAG_(y) +#define OFFSET3(x,y,z) \ + (SGN_(x) << 13) | 010000 | MAG_(x), \ + (SGN_(y) << 13) | 010000 | MAG_(y), \ + (SGN_(z) << 13) | 010000 | (MAG_(z) << 2) + +/* graphplot X data: */ +#define GRAPHX(i,x) \ + i | (x) + +/* graphplot Y data: */ +#define GRAPHY(i,y) \ + i | (y) + +/* basic long vector data (VS60): */ +#define BLVECT(i,dir,len) \ + i | ((dir) << 11) | 02000 | (len) + +/* relative point data: */ +#define RPOINT(i,dx,dy) \ + i | (SGN_(dx) << 13) | (MAG_(dx) << 7) | (SGN_(dy) << 6) | MAG_(dy) +#define RPOINT3(i,dx,dy,dz) \ + i | (SGN_(dx) << 13) | (MAG_(dx) << 7) | (SGN_(dy) << 6) | MAG_(dy), \ + (SGN_(dz) << 13) | (MAG_(dz) << 2) + +/* basic short vector data (VS60): */ +#define BSVECT(i,dir1,len1,dir2,len2) \ + i | ((dir2) << 11) | ((len2) << 7) | ((dir1) << 4) | (len1) + +/* circle/arc data (VS60, option): */ +#define ARC(i,dcx,dcy,dex,dey) \ + i | (SGN_(dcx) << 13) | MAG_(dcx), \ + (SGN_(dcy) << 13) | MAG_(dcy), \ + (SGN_(dex) << 13) | MAG_(dex), \ + (SGN_(dey) << 13) | MAG_(dey) +#define ARC3(i,dcx,dcy,cz,dex,dey,ez) \ + i | (SGN_(dcx) << 13) | MAG_(dcx), \ + (SGN_(dcy) << 13) | MAG_(dcy), \ + (SGN_(cz) << 13) | (MAG_(cz) << 2), \ + (SGN_(dex) << 13) | MAG_(dex), \ + (SGN_(dey) << 13) | MAG_(dey), \ + (SGN_(ez) << 13) | (MAG_(ez) << 2) + +/* absolute vector data (VS60): */ +#define AVECT(i,x,y) \ + i | (SGN_(x) << 13) | MAG_(x), \ + (SGN_(y) << 13) | MAG_(y) +#define AVECT3(i,x,y,z) \ + i | (SGN_(x) << 13) | MAG_(x), \ + (SGN_(y) << 13) | MAG_(y), \ + (SGN_(z) << 13) | (MAG_(z) << 2) diff --git a/display/vttest.c b/display/vttest.c new file mode 100644 index 00000000..eb2efc3f --- /dev/null +++ b/display/vttest.c @@ -0,0 +1,1301 @@ +/* + * $Id: vttest.c,v 1.10 2004/02/07 06:31:21 phil Exp $ + * VT11 test + * Phil Budne + * September 13, 2003 + * Substantially revised by Douglas A. Gwyn, 27 Jan. 2004 + * + * XXX -- assumes ASCII host character set + * + * In addition to providing some display tests, this program serves as an + * example of how the VT11/VS60 display processor simulator can be used + * without a PDP-11 simulator. The vt11_cycle() function performs a single + * "instruction cycle" of the display processor, and display_sync() forces + * the graphics changes to appear in the window system; thus these must be + * iterated at a fairly rapid rate to provide reasonable interaction. This + * implies that "host" computation must be kept minimal per iteration, or + * else done in a separate thread. When using multiple threads, the display + * file should be declared with "volatile" qualification to ensure that + * modifications are picked up by the display-processor thread. + * + * Part of the fun of display-file programming is figuring out ways to + * safely modify the display without stopping the display processor, which + * is asynchronously interpreting the display file. + */ +#undef FRAME1STOP /* define to pause after first frame of a section */ + +#ifndef TEST_DIS +#define TEST_DIS DIS_VR48 +#endif + +#ifndef TEST_RES +#define TEST_RES RES_HALF +#endif + +#include +#include + +#include "ws.h" /* for ws_beep() */ +#include "display.h" +#include "vt11.h" +#include "vtmacs.h" + +#define USEC 3 /* simulated microseconds per cycle; + making this large causes flicker! */ + +#define JMPA 0160000 /* first word of DJMP_ABS */ + +#define SUPSCR 021 /* SUPERSCRIPT char */ +#define SUBSCR 022 /* SUBSCRIPT char */ +#define ENDSUP 023 /* END SUPERSCRIPT char */ +#define ENDSUB 024 /* END SUBSCRIPT char */ + +/* The following display file (whose words might be larger than 16 bits) is + divided into sections, each ended by a display-stop-with-interrupt + instruction followed by an extra word. The display-stop interrupt handler + replaces these two words with a jump to the start of the section, causing + an endless refresh loop. To advance to the next section, activate the + "tip switch" (mouse button 1); this works even if simulating a VT11. */ + +#define ENDSECT LSRA(ST_STOP,SI_GENERATE,LI_SAME,IT_SAME,RF_UNSYNC,MN_SAME), 0, +#define ENDFILE LSRA(ST_STOP,SI_GENERATE,LI_SAME,IT_SAME,RF_UNSYNC,MN_SAME), 1, + +/* FILE VT. Static displays that work for both VT11 and VS60. */ + +unsigned short VT[] = { + /* SECTION 1. Box just inside VR14 area using all four line types. + Suitable for VT11 and VS60. */ + + LSRA(ST_SAME, SI_SAME, LI_INTENSIFY, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_5, LP_ENA, BL_OFF, LT_SAME), + APOINT(I_OFF, 0, 0), + + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_LDASH), + LVECT(I_ON, 01777, 0), + + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SDASH), + LVECT(I_ON, 0, 01377), + + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_DDASH), + LVECT(I_ON, -01777, 0), + + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + LVECT(I_ON, 0, -01377), + + ENDSECT + + /* SECTION 2. All text characters (both normal and italic). + Suitable for VT11 and VS60. */ + + LSRA(ST_SAME, SI_SAME, LI_INTENSIFY, IT_NORMAL, RF_UNSYNC, MN_SAME), + + /* normal text */ + SGM(GM_APOINT, IN_7, LP_ENA, BL_OFF, LT_SAME), + APOINT(I_OFF, 0, 736), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + CHAR(' ',' '), CHAR('A','B'), CHAR('C','D'), CHAR('E','F'), CHAR('G','H'), + CHAR('I','J'), CHAR('K','L'), CHAR('M','N'), CHAR('O','P'), CHAR('Q','R'), + CHAR('S','T'), CHAR('U','V'), CHAR('W','X'), CHAR('Y','Z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('a','b'), CHAR('c','d'), CHAR('e','f'), CHAR('g','h'), + CHAR('i','j'), CHAR('k','l'), CHAR('m','n'), CHAR('o','p'), CHAR('q','r'), + CHAR('s','t'), CHAR('u','v'), CHAR('w','x'), CHAR('y','z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('0','1'), CHAR('2','3'), CHAR('4','5'), CHAR('6','7'), + CHAR('8','9'), CHAR(' ','!'), CHAR('"','#'), CHAR('$','%'), CHAR('&','\''), + CHAR('(',')'), CHAR('*','+'), CHAR(',','-'), CHAR('.','/'), CHAR('@',0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(':',';'), CHAR('<','='), CHAR('>','?'), CHAR('[','\\'), + CHAR(']','^'), CHAR('_','`'), CHAR('{','|'), CHAR('}','~'), CHAR(127,0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(14,0), CHAR(1,2), CHAR(3,4), CHAR(5,6), CHAR(7,8), + CHAR(9,10), CHAR(11,12), CHAR(13,14), CHAR(16,17), CHAR(18,19), CHAR(20,21), + CHAR(22,23), CHAR(24,25), CHAR(26,27), CHAR(28,29), CHAR(30,31), + CHAR(15,0), CHAR('\r','\n'), + + /* italic text */ + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_ITALIC, RF_UNSYNC, MN_SAME), + + /* note no SGMhere */ + CHAR(' ',' '), CHAR('A','B'), CHAR('C','D'), CHAR('E','F'), CHAR('G','H'), + CHAR('I','J'), CHAR('K','L'), CHAR('M','N'), CHAR('O','P'), CHAR('Q','R'), + CHAR('S','T'), CHAR('U','V'), CHAR('W','X'), CHAR('Y','Z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('a','b'), CHAR('c','d'), CHAR('e','f'), CHAR('g','h'), + CHAR('i','j'), CHAR('k','l'), CHAR('m','n'), CHAR('o','p'), CHAR('q','r'), + CHAR('s','t'), CHAR('u','v'), CHAR('w','x'), CHAR('y','z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('0','1'), CHAR('2','3'), CHAR('4','5'), CHAR('6','7'), + CHAR('8','9'), CHAR(' ','!'), CHAR('"','#'), CHAR('$','%'), CHAR('&','\''), + CHAR('(',')'), CHAR('*','+'), CHAR(',','-'), CHAR('.','/'), CHAR('@',0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(':',';'), CHAR('<','='), CHAR('>','?'), CHAR('[','\\'), + CHAR(']','^'), CHAR('_','`'), CHAR('{','|'), CHAR('}','~'), CHAR(127,0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(14,0), CHAR(1,2), CHAR(3,4), CHAR(5,6), CHAR(7,8), + CHAR(9,10), CHAR(11,12), CHAR(13,14), CHAR(16,17), CHAR(18,19), CHAR(20,21), + CHAR(22,23), CHAR(24,25), CHAR(26,27), CHAR(28,29), CHAR(30,31), + CHAR(15,0), CHAR('\r','\n'), + + ENDSECT + + /* SECTION 3. Fancy display involving all VT11 graphic modes. + Suitable for VT11 and VS60. */ + + LSRA(ST_SAME, SI_SAME, LI_INTENSIFY, IT_NORMAL, RF_UNSYNC, MN_SAME), + + /* normal text */ + SGM(GM_APOINT, IN_4, LP_ENA, BL_OFF, LT_SAME), + APOINT(I_OFF, 0, 01340), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + CHAR(' ',' '), CHAR('A','B'), CHAR('C','D'), CHAR('E','F'), CHAR('G','H'), + CHAR('I','J'), CHAR('K','L'), CHAR('M','N'), CHAR('O','P'), CHAR('Q','R'), + CHAR('S','T'), CHAR('U','V'), CHAR('W','X'), CHAR('Y','Z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('a','b'), CHAR('c','d'), CHAR('e','f'), CHAR('g','h'), + CHAR('i','j'), CHAR('k','l'), CHAR('m','n'), CHAR('o','p'), CHAR('q','r'), + CHAR('s','t'), CHAR('u','v'), CHAR('w','x'), CHAR('y','z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('0','1'), CHAR('2','3'), CHAR('4','5'), CHAR('6','7'), + CHAR('8','9'), CHAR(' ','!'), CHAR('"','#'), CHAR('$','%'), CHAR('&','\''), + CHAR('(',')'), CHAR('*','+'), CHAR(',','-'), CHAR('.','/'), CHAR('@',0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(':',';'), CHAR('<','='), CHAR('>','?'), CHAR('[','\\'), + CHAR(']','^'), CHAR('_','`'), CHAR('{','|'), CHAR('}','~'), CHAR(127,0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(14,0), CHAR(1,2), CHAR(3,4), CHAR(5,6), CHAR(7,8), + CHAR(9,10), CHAR(11,12), CHAR(13,14), CHAR(16,17), CHAR(18,19), CHAR(20,21), + CHAR(22,23), CHAR(24,25), CHAR(26,27), CHAR(28,29), CHAR(30,31), + CHAR(15,0), CHAR('\r','\n'), + + /* italic text */ + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_ITALIC, RF_UNSYNC, MN_SAME), + + /* note no SGMhere */ + CHAR(' ',' '), CHAR('A','B'), CHAR('C','D'), CHAR('E','F'), CHAR('G','H'), + CHAR('I','J'), CHAR('K','L'), CHAR('M','N'), CHAR('O','P'), CHAR('Q','R'), + CHAR('S','T'), CHAR('U','V'), CHAR('W','X'), CHAR('Y','Z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('a','b'), CHAR('c','d'), CHAR('e','f'), CHAR('g','h'), + CHAR('i','j'), CHAR('k','l'), CHAR('m','n'), CHAR('o','p'), CHAR('q','r'), + CHAR('s','t'), CHAR('u','v'), CHAR('w','x'), CHAR('y','z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('0','1'), CHAR('2','3'), CHAR('4','5'), CHAR('6','7'), + CHAR('8','9'), CHAR(' ','!'), CHAR('"','#'), CHAR('$','%'), CHAR('&','\''), + CHAR('(',')'), CHAR('*','+'), CHAR(',','-'), CHAR('.','/'), CHAR('@',0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(':',';'), CHAR('<','='), CHAR('>','?'), CHAR('[','\\'), + CHAR(']','^'), CHAR('_','`'), CHAR('{','|'), CHAR('}','~'), CHAR(127,0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(14,0), CHAR(1,2), CHAR(3,4), CHAR(5,6), CHAR(7,8), + CHAR(9,10), CHAR(11,12), CHAR(13,14), CHAR(16,17), CHAR(18,19), CHAR(20,21), + CHAR(22,23), CHAR(24,25), CHAR(26,27), CHAR(28,29), CHAR(30,31), + CHAR(15,0), CHAR('\r','\n'), + + /* labeled lines of all types, blinks, and intensities (LP intr disabled) */ + LSRA(ST_SAME, SI_SAME, LI_INTENSIFY, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_SAME, LP_DIS, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0740), + + SGM(GM_CHAR, IN_0, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('0',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0740), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0740), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0740), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0700), + + SGM(GM_CHAR, IN_1, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('1',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0700), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0700), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0700), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0640), + + SGM(GM_CHAR, IN_2, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('2',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0640), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0640), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0640), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0600), + + SGM(GM_CHAR, IN_3, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('3',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0600), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0600), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0600), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0540), + + SGM(GM_CHAR, IN_4, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('4',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0540), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0540), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0540), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0500), + + SGM(GM_CHAR, IN_5, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('5',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0500), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0500), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0500), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0440), + + SGM(GM_CHAR, IN_6, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('6',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0440), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0440), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0440), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0400), + + SGM(GM_CHAR, IN_7, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('7',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0400), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0400), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0400), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + /* similar, but LP intr enabled, official threshold intensities */ + SGM(GM_APOINT, IN_SAME, LP_ENA, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0340), + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_ITALIC, RF_UNSYNC, MN_SAME), + + SGM(GM_CHAR, IN_6, LP_SAME, BL_ON, LT_SAME), + CHAR('I','N'), CHAR('T','R'), + + SGM(GM_APOINT, IN_4, LP_SAME, BL_OFF, LT_SAME), + APOINT(I_ON, 0140, 0340), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0340), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0340), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + /* graphplots */ + SGM(GM_APOINT, IN_5, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 040, 0200), + + LSRB(CL_SAME, SS_CHANGE, 040), + + SGM(GM_GRAPHY, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + GRAPHY(I_ON, 0160), + GRAPHY(I_ON, 0140), + GRAPHY(I_ON, 0120), + GRAPHY(I_ON, 0100), + GRAPHY(I_ON, 0060), + GRAPHY(I_ON, 0040), + + SGM(GM_RPOINT, IN_SAME, LP_SAME, BL_OFF, LT_SAME), + RPOINT(I_OFF, 0040, 0), + RPOINT(I_ON, 0040, 0), + + LSRB(CL_SAME, SS_CHANGE, 020), + + SGM(GM_GRAPHX, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + GRAPHX(I_ON, 0500), + GRAPHX(I_ON, 0540), + GRAPHX(I_ON, 0600), + GRAPHX(I_ON, 0640), + GRAPHX(I_ON, 0700), + GRAPHX(I_ON, 0740), + + /* long vectors in all directions from a common origin */ + SGM(GM_APOINT, IN_4, LP_SAME, BL_SAME, LT_SOLID), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, 0100), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, 0200), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0300, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0200, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0100, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0100, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0200, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0300, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, 0200), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, 0100), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, -0100), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, -0200), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0300, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0200, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0100, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0100, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0200, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0300, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, -0200), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, -0100), + + /* nearby lines with varied spacing */ + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01200, 0500), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -1), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -2), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -3), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -4), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -5), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -6), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -7), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -010), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, 044), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 1, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 2, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 3, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 4, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 5, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 6, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 7, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 010, 077), + SVECT(I_ON, 0, -077), + + /* all four flavors of characters (lp intr enabled, but intensity 4) */ + + SGM(GM_APOINT, IN_4, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01040, 0240), + + LSRA(ST_SAME, SI_SAME, LP_SAME, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_OFF, LT_SAME), + CHAR('N','o'), CHAR('r','m'), CHAR('a','l'), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_ON, LT_SAME), + CHAR(' ','B'), CHAR('l','i'), CHAR('n','k'), + + SGM(GM_APOINT, IN_4, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01040, 0200), + + LSRA(ST_SAME, SI_SAME, LP_SAME, IT_ITALIC, RF_UNSYNC, MN_SAME), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','t'), CHAR('a','l'), CHAR('i','c'), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_ON, LT_SAME), + CHAR(' ','B'), CHAR('l','i'), CHAR('n','k'), + + /* all eight intensities of characters (lp intr enabled) */ + + LSRA(ST_SAME, SI_SAME, LP_SAME, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_5, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01040, 0100), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), + + SGM(GM_CHAR, IN_0, LP_SAME, BL_SAME, LT_SAME), + CHAR('0',0), + SGM(GM_CHAR, IN_1, LP_SAME, BL_SAME, LT_SAME), + CHAR('1',0), + SGM(GM_CHAR, IN_2, LP_SAME, BL_SAME, LT_SAME), + CHAR('2',0), + SGM(GM_CHAR, IN_3, LP_SAME, BL_SAME, LT_SAME), + CHAR('3',0), + SGM(GM_CHAR, IN_4, LP_SAME, BL_SAME, LT_SAME), + CHAR('4',0), + SGM(GM_CHAR, IN_5, LP_SAME, BL_SAME, LT_SAME), + CHAR('5',0), + SGM(GM_CHAR, IN_6, LP_SAME, BL_SAME, LT_SAME), + CHAR('6',0), + SGM(GM_CHAR, IN_7, LP_SAME, BL_SAME, LT_SAME), + CHAR('7',0), + + /* XXX -- more can be included in this pattern */ + + ENDSECT + + /* SECTION 4. Clipping tests. + Suitable for VT11 and VS60. */ + + LSRA(ST_SAME, SI_SAME, LI_INTENSIFY, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01000, 01000), + + SGM(GM_LVECT, IN_4, LP_ENA, BL_OFF, LT_SOLID), + LVECT(I_ON, 01100, 0), + LVECT(I_ON, -01100, 01100), + LVECT(I_ON, 0, -01100), + LVECT(I_OFF, 0, 01100), + LVECT(I_ON, -01100, -01100), + LVECT(I_ON, 01100, 0), + LVECT(I_ON, 0, -01100), + LVECT(I_ON, -01100, 01100), + LVECT(I_OFF, 01100, 0), + LVECT(I_OFF, 01100, 0), + LVECT(I_ON, -01100, -01100), + + ENDSECT + + /* END OF TEST SECTIONS. */ + + ENDFILE +}; + +/* FILE LP. Dynamic light pen tracking; works for both VT11 and VS60. */ + +unsigned short LP[] = { + /* SECTION 1. "rubber-band" dot-dash vector to tracking object. */ + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01000, 01000), /* screen center */ + + SGM(GM_LVECT, IN_4, LP_DIS, BL_SAME, LT_DDASH), + /* following coordinates are updated by LP hit intr. handler: */ + LVECT(I_ON, 0, 0), /* tracking object center */ + + SGM(GM_SVECT, IN_7, LP_ENA, BL_SAME, LT_SOLID), + SVECT(I_OFF, 1, -31), + SVECT(I_ON, -2, 0), + SVECT(I_OFF, 2, 0), + SVECT(I_ON, 0, 62), + SVECT(I_ON, -2, 0), + SVECT(I_OFF, 2, 0), + SVECT(I_ON, 30, -30), + SVECT(I_OFF, 0, -2), + SVECT(I_ON, 0, 2), + SVECT(I_ON, -62, 0), + SVECT(I_OFF, 0, -2), + SVECT(I_ON, 0, 2), + SVECT(I_ON, 30, 30), + SVECT(I_ON, 0, -62), + SVECT(I_ON, -30, 30), + SVECT(I_ON, 62, 0), + SVECT(I_ON, -30, -30), +#if 0 /* not needed for this app */ + SVECT(I_OFF, -1, 31), /* "flyback" vector */ +#endif + + ENDSECT + + /* END OF TEST SECTIONS. */ + + ENDFILE +}; + +/* FILE VS. Static displays that work only for VS60. */ + +unsigned short VS[] = { + /* SECTION 0. Warning that VS60 is required. */ + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_7, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0300, 01000), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_OFF, LT_SAME), + CHAR('F','o'), CHAR('l','l'), CHAR('o','w'), CHAR('i','n'), CHAR('g',' '), + CHAR('t','e'), CHAR('s','t'), CHAR('s',' '), CHAR('d','o'), + CHAR(' ','n'), CHAR('o','t'), CHAR(' ','w'), CHAR('o','r'), + CHAR('k',' '), CHAR('f','o'), CHAR('r',' '), CHAR('V','T'), + CHAR('1','1'), CHAR(';',0), + + /* italic text */ + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_ITALIC, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_7, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0340, 00720), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_ON, LT_SAME), + CHAR('S','T'), CHAR('O','P'), CHAR(' ','P'), CHAR('R','O'), CHAR('G','R'), + CHAR('A','M'), CHAR(' ','i'), CHAR('f',' '), CHAR('n','o'), CHAR('t',' '), + CHAR('u','s'), CHAR('i','n'), CHAR('g',' '), CHAR('V','R'), CHAR('4','8'), + CHAR('!',0), + + ENDSECT + + /* SECTION 1. Variety of text characters. */ + + LSRA(ST_SAME, SI_SAME, LI_BRIGHTDOWN, IT_SAME, RF_UNSYNC, MN_MAIN), + + /* horizontal text, 4 sizes */ + SGM(GM_APOINT, IN_4, LP_ENA, BL_SAME, LT_SAME), + APOINT(I_OFF, 0, 01600), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LSRC(RO_SAME, CS_CHANGE, 0, VS_SAME, 0), + CHAR(' ',' '), CHAR('S','m'), CHAR('a','l'), CHAR('l',':'), CHAR(' ','1'), + CHAR('/','2'), + LSRC(RO_SAME, CS_CHANGE, 1, VS_SAME, 0), + CHAR(' ',' '), CHAR('N','o'), CHAR('r','m'), CHAR('a','l'), CHAR(':',' '), + CHAR('1',0), + LSRC(RO_SAME, CS_CHANGE, 2, VS_SAME, 0), + CHAR(' ',' '), CHAR('B','i'), CHAR('g',':'), CHAR(' ','1'), CHAR('-','1'), + CHAR('/','2'), + LSRC(RO_SAME, CS_CHANGE, 3, VS_SAME, 0), + CHAR(' ',' '), CHAR('L','a'), CHAR('r','g'), CHAR('e',':'), CHAR(' ','2'), + CHAR('\r','\n'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('A',SUBSCR), CHAR('B',SUBSCR), CHAR('C',SUBSCR), + CHAR('D',ENDSUB), CHAR(ENDSUB,ENDSUB), CHAR('W',SUPSCR), + CHAR('X',SUPSCR), CHAR('Y',SUPSCR), CHAR('Z',ENDSUP), + CHAR(ENDSUP,ENDSUP), CHAR('!','!'), + + /* vertical text, 4 sizes */ + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0200, 0), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LSRC(RO_VERTICAL, CS_CHANGE, 0, VS_SAME, 0), + CHAR(' ',' '), CHAR('S','m'), CHAR('a','l'), CHAR('l',':'), CHAR(' ','1'), + CHAR('/','2'), + LSRC(RO_SAME, CS_CHANGE, 1, VS_SAME, 0), + CHAR(' ',' '), CHAR('N','o'), CHAR('r','m'), CHAR('a','l'), CHAR(':',' '), + CHAR('1',0), + LSRC(RO_SAME, CS_CHANGE, 2, VS_SAME, 0), + CHAR(' ',' '), CHAR('B','i'), CHAR('g',':'), CHAR(' ','1'), CHAR('-','1'), + CHAR('/','2'), + LSRC(RO_SAME, CS_CHANGE, 3, VS_SAME, 0), + CHAR(' ',' '), CHAR('L','a'), CHAR('r','g'), CHAR('e',':'), CHAR(' ','2'), + CHAR('\r','\n'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('A',SUBSCR), CHAR('B',SUBSCR), CHAR('C',SUBSCR), + CHAR('D',ENDSUB), CHAR(ENDSUB,ENDSUB), CHAR('W',SUPSCR), + CHAR('X',SUPSCR), CHAR('Y',SUPSCR), CHAR('Z',ENDSUP), + CHAR(ENDSUP,ENDSUP), CHAR('!','!'), + + /* horizontal text, sub/superscript examples from DECgraphic-11 manual */ + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0400, 01200), + + LSRC(RO_HORIZONTAL, CS_CHANGE, 2, VS_SAME, 0), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('C',SUBSCR), CHAR('2',ENDSUB), CHAR('H',SUBSCR), CHAR('5',ENDSUB), + CHAR('O','H'), CHAR(' ',' '), + CHAR(016,000), CHAR(017,'='), CHAR(016,003), CHAR(017,'('), + CHAR('x',SUBSCR), CHAR('i',ENDSUB), CHAR('-','q'), CHAR(SUBSCR,'i'), + CHAR(ENDSUB,')'), CHAR(SUPSCR,'2'), CHAR(ENDSUP,'e'), CHAR(SUPSCR,'-'), + CHAR('i',SUPSCR), CHAR('2',ENDSUP), CHAR(ENDSUP,0), + + LSRC(RO_SAME, CS_CHANGE, 1, VS_SAME, 0), + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_SAME, RF_SAME, MN_MENU), + SGM(GM_APOINT, IN_7, LP_ENA, BL_SAME, LT_SAME), + APOINT(I_OFF, 0, 1000), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('U','n'), CHAR('s','y'), CHAR('n','c'), + + ENDSECT + + /* SECTION 2. Basic vectors (long and short). */ + + LSRA(ST_SAME, SI_SAME, LI_BRIGHTDOWN, IT_SAME, RF_40, MN_MAIN), + + SGM(GM_APOINT, IN_4, LP_ENA, BL_OFF, LT_SDASH), + APOINT(I_OFF, 01000, 01000), + + SGM(GM_GRAPHX, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + BLVECT(I_OFF, 2, 0600), + BLVECT(I_ON, 0, 0200), + BLVECT(I_ON, 7, 0400), + BLVECT(I_ON, 6, 0400), + BLVECT(I_ON, 5, 0400), + BLVECT(I_ON, 4, 0400), + SGM(GM_GRAPHY, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + BLVECT(I_ON, 3, 0400), + BLVECT(I_ON, 2, 0400), + BLVECT(I_ON, 1, 0400), + BLVECT(I_ON, 0, 0200), + BLVECT(I_OFF, 6, 0600), + + SGM(GM_BSVECT, IN_SAME, LP_SAME, BL_ON, LT_SOLID), + BSVECT(I_OFF, 2, 007, 2, 016), + BSVECT(I_ON, 0, 007, 7, 016), + BSVECT(I_ON, 6, 016, 5, 016), + BSVECT(I_ON, 4, 016, 3, 016), + BSVECT(I_ON, 2, 016, 1, 016), + BSVECT(I_ON, 0, 007, 0, 000), + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_SAME, RF_SAME, MN_MENU), + SGM(GM_APOINT, IN_7, LP_ENA, BL_OFF, LT_SAME), + APOINT(I_OFF, 0, 1000), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('4','0'), CHAR('H','z'), CHAR(' ','S'), CHAR('y','n'), CHAR('c',0), + + ENDSECT + + /* SECTION 3. 3D data, but depth cueing disabled. */ + + LSRBB(ZD_YES, ED_ENA, DQ_OFF, ES_YES), /* but term char not used */ + LSRA(ST_SAME, SI_SAME, LI_BRIGHTDOWN, IT_SAME, RF_30, MN_MAIN), + + SGM(GM_APOINT, IN_4, LP_ENA, BL_OFF, LT_LDASH), + APOINT3(I_OFF, 0200, 0200, 0400), + + SGM(GM_AVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + AVECT3(I_ON, 01200, 00200, 0400), + AVECT3(I_ON, 01200, 01200, 0400), + AVECT3(I_ON, 00200, 01200, 0400), + AVECT3(I_ON, 00200, 00200, 0400), + AVECT3(I_OFF, 00600, 00600, -0400), + AVECT3(I_ON, 01600, 00600, -0400), + AVECT3(I_ON, 01600, 01600, -0400), + AVECT3(I_ON, 00600, 01600, -0400), + AVECT3(I_ON, 00600, 00600, -0400), + SGM(GM_AVECT, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + AVECT3(I_ON, 00200, 00200, 0400), + AVECT3(I_OFF, 01200, 00200, 0400), + AVECT3(I_ON, 01600, 00600, -0400), + AVECT3(I_OFF, 01600, 01600, -0400), + AVECT3(I_ON, 01200, 01200, 0400), + AVECT3(I_OFF, 00200, 01200, 0400), + AVECT3(I_ON, 00600, 01600, -0400), + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_SAME, RF_SAME, MN_MENU), + SGM(GM_APOINT, IN_7, LP_ENA, BL_OFF, LT_SAME), + APOINT3(I_OFF, 0, 1000, 0200), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('3','0'), CHAR('H','z'), CHAR(' ','S'), CHAR('y','n'), CHAR('c',0), + + ENDSECT + + /* SECTION 4. 3D data, with depth cueing enabled. */ + + LSRBB(ZD_YES, ED_ENA, DQ_ON, ES_YES), /* but term char not used */ + LSRA(ST_SAME, SI_SAME, LI_BRIGHTDOWN, IT_SAME, RF_EXT, MN_MAIN), + + SGM(GM_APOINT, IN_4, LP_ENA, BL_OFF, LT_DDASH), + APOINT3(I_OFF, 0200, 0200, 0400), + + SGM(GM_AVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + AVECT3(I_ON, 01200, 00200, 0400), + AVECT3(I_ON, 01200, 01200, 0400), + AVECT3(I_ON, 00200, 01200, 0400), + AVECT3(I_ON, 00200, 00200, 0400), + AVECT3(I_OFF, 00600, 00600, -0400), + AVECT3(I_ON, 01600, 00600, -0400), + AVECT3(I_ON, 01600, 01600, -0400), + AVECT3(I_ON, 00600, 01600, -0400), + AVECT3(I_ON, 00600, 00600, -0400), + SGM(GM_AVECT, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + AVECT3(I_ON, 00200, 00200, 0400), + AVECT3(I_OFF, 01200, 00200, 0400), + AVECT3(I_ON, 01600, 00600, -0400), + AVECT3(I_OFF, 01600, 01600, -0400), + AVECT3(I_ON, 01200, 01200, 0400), + AVECT3(I_OFF, 00200, 01200, 0400), + AVECT3(I_ON, 00600, 01600, -0400), + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_SAME, RF_SAME, MN_MENU), + SGM(GM_APOINT, IN_7, LP_ENA, BL_OFF, LT_SAME), + APOINT3(I_OFF, 0, 1000, 0200), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('E','x'), CHAR('t','.'), CHAR(' ','S'), CHAR('y','n'), CHAR('c',0), + + ENDSECT + + /* SECTION 5. Circles and arcs. */ + + SGM(GM_APOINT, IN_4, LP_ENA, BL_ON, LT_SOLID), + APOINT(I_OFF, 0500, 01400), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, -0100, 0, 0, 0), + + SGM(GM_APOINT, IN_5, LP_SAME, BL_OFF, LT_SDASH), + APOINT(I_OFF, 0532, 01532), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, -0132, -0132, 0, -0264), + + SGM(GM_APOINT, IN_6, LP_SAME, BL_SAME, LT_LDASH), + APOINT(I_OFF, 0400, 01700), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, 0, -0300, 0, -0600), + + SGM(GM_APOINT, IN_7, LP_SAME, BL_SAME, LT_DDASH), + APOINT(I_OFF, 0114, 01664), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, 0264, -0264, 0, -0550), + + SGM(GM_APOINT, IN_4, LP_SAME, BL_SAME, LT_SOLID), + APOINT(I_OFF, 01400, 01400), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, 0, 0, 0400, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SDASH), + APOINT(I_OFF, 0500, 0400), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, -0100, 0, 0200, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01600, 0400), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + ARC(I_ON, -0200, 0, -0200, 0300), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SDASH), + ARC(I_ON, 0, -0300, -0200, -0300), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_LDASH), + ARC(I_ON, 0200, 0, 0200, -0300), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_DDASH), + ARC(I_ON, 0, 0300, 0200, 0300), + + ENDSECT + + /* SECTION 6. Display subroutines, with and without parameter restore. */ + + /* XXX need test for subroutines */ + + /* SECTION 7. Offset, vector scale, and clipping. */ + + /* XXX need test for offset, vector scale, and clipping */ + + /* END OF TEST SECTIONS. */ + + ENDFILE +}; + +static unsigned short *df; /* -> start of current display file */ +static uint16 start; /* initial DPC for section of d.file */ +static int more; /* set until end of d.file seen */ + +int +main(void) { + int c; + + vt11_display = TEST_DIS; + vt11_scale = TEST_RES; + + /* VT11/VS60 tests */ + + puts("initial tests work for both VT11 and VS60"); + for (df = VT, start = 0, more = 1; more; ) { + vt11_reset(); /* reset everything */ + vt11_set_dpc(start); /* start section */ + c = 0; + while (vt11_cycle(USEC, 1)) { + display_sync(); /* XXX push down? */ + if (display_lp_sw) /* tip switch activated */ + c = 1; /* flag: break requested */ + if (c && !display_lp_sw) /* wait for switch release */ + break; + } + /* end of section */ + } + /* end of display file */ + + /* light pen tracking */ + + ws_beep(); + puts("move the light pen through the tracking object"); + fflush(stdout); + for (df = LP, start = 0, more = 1; more; ) { + vt11_reset(); /* reset everything */ + vt11_set_dpc(start); /* start section */ + c = 0; + while (vt11_cycle(USEC, 1)) { + display_sync(); /* XXX push down? */ + if (display_lp_sw) /* tip switch activated */ + c = 1; /* flag: break requested */ + if (c && !display_lp_sw) /* wait for switch release */ + break; + /* [dynamic modifications to the display file can be done here] */ + } + /* end of section */ + } + /* end of display file */ + + /* VS60 tests */ + + ws_beep(); + puts("following tests require VS60"); + for (df = VS, start = 0, more = 1; more; ) { + vt11_reset(); /* reset everything */ + vt11_set_str((uint16)(0200 | '~')); /* set terminating char. */ + vt11_set_anr((uint16)(040000 | (2<<12) | 04000 | 01234)); + /* set associative name 0123x */ + vt11_set_dpc(start); /* start section */ + c = 0; + while (vt11_cycle(USEC, 1)) { + display_sync(); /* XXX push down? */ + if (display_lp_sw) /* tip switch activated */ + c = 1; /* flag: break requested */ + if (c && !display_lp_sw) /* wait for switch release */ + break; + } + /* end of section */ + } + /* end of display file */ + + /* XXX would be nice to have an example of animation */ + + return 0; +} + +/* + * callbacks from display.c + */ +unsigned long +cpu_get_switches(void) { + return 0; +} + +void +cpu_set_switches(unsigned long bits) { +} + +/* + * callbacks from vt11.c + */ + +int +vt_fetch(uint32 addr, vt11word *w) { + *w = df[addr/2]; + return 0; +} + +void +vt_stop_intr(void) { + uint16 dpc = vt11_get_dpc(); /* -> just after DSTOP instruction */ + if (df[dpc/2] == 0) { /* ENDSECT */ +#ifdef FRAME1STOP + int c; + puts("end of first pass through this test pattern; display frozen"); + puts("enter newline to refresh this section or EOF to quit"); + fflush(stdout); + while ((c = getchar()) != '\n') + if (c == EOF) + exit(0); /* user aborted test */ +#endif + df[dpc/2 - 1] = JMPA; + df[dpc/2] = start; + start = dpc + 2; /* save start of next section */ + vt11_set_dpc(dpc - 2); /* reset; then JMPA to old start */ + puts("press and release tip switch (mouse button 1) for next display"); + fflush(stdout); + } else /* ENDFILE */ + more = 0; +} + +void +vt_lpen_intr(void) { + if (df == LP) { + int dx = (int)(vt11_get_xpr() & 01777) - 01000; + int dy = (int)(vt11_get_ypr() & 01777) - 01000; + if (dx < 0) + dx = (-dx) | 020000; /* negative */ + if (dy < 0) + dy = (-dy) | 020000; /* negative */ + + df[4] = dx | I_ON; /* visible */ + df[5] = dy; + } else { + printf("VT11 lightpen interrupt (%d,%d)\n", + (int)vt11_get_xpr() & 01777, (int)vt11_get_ypr() & 01777); + fflush(stdout); + } + vt11_set_dpc((uint16)1); /* resume */ +} + +void +vt_char_intr(void) { + puts("VT11 illegal character/timeout interrupt"); + fflush(stdout); + vt11_set_dpc((uint16)1); /* resume */ +} + +void +vt_name_intr(void) { + puts("VS60 name-match interrupt"); + fflush(stdout); + vt11_set_dpc((uint16)1); /* resume */ +} diff --git a/display/win32.c b/display/win32.c new file mode 100644 index 00000000..45c5e453 --- /dev/null +++ b/display/win32.c @@ -0,0 +1,418 @@ +/* + * $Id: win32.c,v 1.38 2004/02/07 06:32:01 phil Exp $ + * Win32 support for XY display simulator + * Phil Budne + * September 2003 + * Revised by Douglas A. Gwyn, 05 Feb. 2004 + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne + * + * 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 + * THE AUTHORS 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 the authors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization + * from the authors. + */ + +/* use a thread to handle windows messages; */ +#define THREADS + +/* + * BUGS: + * Does not allow you to close display window; + * would need to tear down both system, and system independent data. + * + * now tries to handle PAINT message, as yet untested!! + */ + +#include +#include +#include +#include "ws.h" +#include "display.h" + +#ifndef PIX_SIZE +#define PIX_SIZE 1 +#endif + +#define APP_CLASS "XYAppClass" +#define APP_MENU "XYAppMenu" /* ?? */ + +/* + * light pen location + * see ws.h for full description + */ +int ws_lp_x = -1; +int ws_lp_y = -1; + +static HWND static_wh; +static HINSTANCE static_inst; +static int xpixels, ypixels; +static char *window_name; +static HBRUSH white_brush; +static HBRUSH black_brush; +#ifdef SWITCH_CURSORS +static HCURSOR cross, arrow; +#endif + +static __inline int +map_key(int k) +{ + switch (k) { + case 186: return ';'; /* VK_OEM_1? */ + case 222: return '\''; /* VK_OEM_7? */ + } + return k; +} + +static void +keydown(int k) +{ + display_keydown(map_key(k)); +} + +static void +keyup(int k) +{ + display_keyup(map_key(k)); +} + +/* + * here on any button click, or if mouse dragged while a button down + */ +static void +mousepos(DWORD lp) +{ + int x, y; + + x = LOWORD(lp); + y = HIWORD(lp); + + /* convert to display coordinates */ +#if PIX_SIZE > 1 + x /= PIX_SIZE; + y /= PIX_SIZE; +#endif + y = ypixels - 1 - y; + + /* if window has been stretched, can get out of range bits!! */ + if (x >= 0 && x < xpixels && y >= 0 && y < ypixels) { + /* checked by display_add_point() */ + ws_lp_x = x; + ws_lp_y = y; + } +} + +/* thoingggg!! "message for you sir!!!" */ +static LRESULT CALLBACK +patsy(HWND wh, UINT msg, WPARAM wp, LPARAM lp) /* "WndProc" */ +{ + /* printf("msg %d\n", msg); */ + switch (msg) { + case WM_DESTROY: + PostQuitMessage(0); + return 0; + + case WM_MOUSEMOVE: + if (wp & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)) { +#ifdef SWITCH_CURSORS + if (ws_lp_x == -1 && !display_tablet) + SetCursor(cross); +#endif + mousepos(lp); + } +#ifdef SWITCH_CURSORS + else if (ws_lp_x != -1 && !display_tablet) + SetCursor(arrow); +#endif + break; /* return?? */ + + case WM_LBUTTONDOWN: + display_lp_sw = 1; + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: +#ifdef SWITCH_CURSORS + if (!display_tablet) + SetCursor(cross); +#endif + mousepos(lp); + break; /* return?? */ + + case WM_LBUTTONUP: + display_lp_sw = 0; + case WM_MBUTTONUP: + case WM_RBUTTONUP: +#ifdef SWITCH_CURSORS + if (!display_tablet) + SetCursor(arrow); +#endif + ws_lp_x = ws_lp_y = -1; + break; /* return?? */ + + case WM_KEYDOWN: + keydown(wp); + break; + + case WM_KEYUP: + keyup(wp); + break; + + case WM_PAINT: + display_repaint(); + break; /* return?? */ + } + return DefWindowProc(wh, msg, wp, lp); +} + +int +ws_poll(int *valp, int maxus) +{ +#ifdef THREADS + /* msgthread handles window events; just delay simulator */ + if (maxus > 0) + Sleep((maxus+999)/1000); +#else + MSG msg; + DWORD start; + int maxms = (maxus + 999) / 1000; + + for (start = GetTickCount(); GetTickCount() - start < maxms; Sleep(1)) { + /* empty message queue without blocking */ + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +#endif + return 1; +} + +/* called from non-threaded main program */ +int +ws_loop(void (*func)(void *), void *arg) +{ + int val; + while (ws_poll(&val, 0)) + (*func)(arg); + return val; +} + +/* worker for display init */ +static void +ws_init2(void) { + WNDCLASS wc; + int h, w; + +#ifdef SWITCH_CURSORS + if (!display_tablet) { + arrow = LoadCursor(NULL, IDC_ARROW); + cross = LoadCursor(NULL, IDC_CROSS); + } +#endif + + black_brush = GetStockObject(BLACK_BRUSH); + white_brush = GetStockObject(WHITE_BRUSH); + + wc.lpszClassName = APP_CLASS; + wc.lpfnWndProc = patsy; + wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; + /* also CS_NOCLOSE? CS_SAVEBITS? */ + + wc.hInstance = static_inst = GetModuleHandleA(0); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); +#ifdef SWITCH_CURSORS + wc.hCursor = NULL; +#else + wc.hCursor = display_tablet ? NULL : LoadCursor(NULL, IDC_CROSS); +#endif + wc.hbrBackground = black_brush; + wc.lpszMenuName = APP_MENU; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + /* WNDCLASSEX/RegisterClassEx include hIconSm (small icon) */ + RegisterClass(&wc); + + /* + * WS_OVERLAPPEDWINDOW=> + * WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, + * WS_MINIMIZEBOX, WS_MAXIMIZEBOX + * + * WS_CHILD (no menu bar), WS_POPUP (mutually exclusive) + */ + + /* empirical crocks to get entire screen; */ + w = (xpixels*PIX_SIZE)+6; + h = (ypixels*PIX_SIZE)+32; + /* XXX -- above values work with XP; Phil had +10,+30 */ + + static_wh = CreateWindow(APP_CLASS, /* registered class name */ + window_name, /* window name */ + WS_OVERLAPPED, /* style */ + CW_USEDEFAULT, CW_USEDEFAULT, /* X,Y */ + w, h, + NULL, /* HWND hWndParent */ + NULL, /* HMENU hMenu */ + static_inst, /* application instance */ + NULL); /* lpParam */ + + ShowWindow(static_wh, SW_SHOW); + UpdateWindow(static_wh); +} + +#ifdef THREADS +static volatile init_done; +static DWORD msgthread_id; + +static DWORD WINAPI +msgthread(LPVOID arg) +{ + MSG msg; + + ws_init2(); + + /* XXX use a mutex? */ + init_done = 1; + + while (GetMessage(&msg, NULL, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return msg.wParam; +} + +static void +ws_thread_init(void) +{ + HANDLE th = CreateThread(NULL, /* sec. attr. */ + 0, /* stack size */ + msgthread, + NULL, /* param */ + 0, /* flags */ + &msgthread_id); + CloseHandle(th); + + /* XXX use a mutex; don't wait forever!! */ + while (!init_done) + ; +} +#endif /* THREADS */ + +/* called from display layer on first display op */ +int +ws_init(char *name, int xp, int yp, int colors) +{ + xpixels = xp; + ypixels = yp; + window_name = name; + +#ifdef THREADS + ws_thread_init(); +#else + ws_init2(); +#endif + return 1; /* XXX return errors!! */ +} + +void * +ws_color_rgb(int r, int g, int b) +{ + /* XXX check for failure??? try GetNearestColor??? */ + return CreateSolidBrush(RGB(r/256, g/256, b/256)); +} + +void * +ws_color_black(void) +{ + return black_brush; +} + +void * +ws_color_white(void) +{ + return white_brush; +} + +void +ws_display_point(int x, int y, void *color) +{ + HDC dc; + RECT r; + HBRUSH brush = color; + + if (x > xpixels || y > ypixels) + return; + + y = ypixels - 1 - y; /* invert y, top left origin */ + + /* top left corner */ + r.left = x*PIX_SIZE; + r.top = y*PIX_SIZE; + + /* bottom right corner, non-inclusive */ + r.right = (x+1)*PIX_SIZE; + r.bottom = (y+1)*PIX_SIZE; + + if (brush == NULL) + brush = black_brush; + + dc = GetDC(static_wh); + FillRect(dc, &r, brush); + ReleaseDC(static_wh, dc); +} + +void +ws_sync(void) { + /* noop */ +} + +void +ws_beep(void) { +#if 0 + /* play SystemDefault sound; does not work over terminal service */ + MessageBeep(MB_OK); +#else + /* works over terminal service? Plays default sound/beep on Win9x/ME */ + Beep(440, 500); /* Hz, ms. */ +#endif +} + +unsigned long +os_elapsed(void) +{ + static int new; + unsigned long ret; + static DWORD t[2]; + + /* + * only returns milliseconds, but Sleep() + * only takes milliseconds. + * + * wraps after 49.7 days of uptime. + * DWORD is an unsigned long, so this should be OK + */ + t[new] = GetTickCount(); + if (t[!new] == 0) + ret = ~0L; /* +INF */ + else + ret = (t[new] - t[!new]) * 1000; + new = !new; /* Ecclesiastes III */ + return ret; +} diff --git a/display/ws.h b/display/ws.h new file mode 100644 index 00000000..f27cdfa6 --- /dev/null +++ b/display/ws.h @@ -0,0 +1,66 @@ +/* + * $Id: ws.h,v 1.17 2004/02/03 21:23:51 phil Exp $ + * Interfaces to window-system specific code for XY display simulation + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne + * + * 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 + * THE AUTHORS 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 the authors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization + * from the authors. + */ + +/* unless you're writing a new driver, you shouldn't be looking here! */ + +extern int ws_init(char *, int, int, int); +extern void ws_close(void); // 2006-07-19 SAI +extern void *ws_color_rgb(int, int, int); +extern void *ws_color_black(void); +extern void *ws_color_white(void); +extern void ws_display_point(int, int, void *); +extern void ws_sync(void); +extern int ws_loop(void (*)(void *), void *); +extern int ws_poll(int *, int); +extern void ws_beep(void); + +/* entries into display.c from below: */ +extern void display_keyup(int); +extern void display_keydown(int); +extern void display_repaint(void); + +/* + * Globals set by O/S display level to SCALED location in display + * coordinate system in order to save an upcall on every mouse + * movement. + * + * *NOT* for consumption by clients of display.c; although display + * clients can now get the scaling factor, real displays only give you + * a light pen "hit" when the beam passes under the light pen. + */ + +extern int ws_lp_x, ws_lp_y; + +/* + * O/S services in theory independent of window system, + * but in (current) practice not! + */ +extern unsigned long os_elapsed(void); diff --git a/display/x11.c b/display/x11.c new file mode 100644 index 00000000..e04709df --- /dev/null +++ b/display/x11.c @@ -0,0 +1,512 @@ +/* + * $Id: x11.c,v 1.29 2004/02/03 21:23:51 phil Exp $ + * X11 support for XY display simulator + * Phil Budne + * September 2003 + * + * Changes from Douglas A. Gwyn, Jan 8, 2004 + * + * started from PDP-8/E simulator (vc8e.c & kc8e.c); + * This PDP8 Emulator was written by Douglas W. Jones at the + * University of Iowa. It is distributed as freeware, of + * uncertain function and uncertain utility. + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne + * + * 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 + * THE AUTHORS 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 the authors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization + * from the authors. + */ + +#include +#include +#include +#include "ws.h" +#include "display.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef PIX_SIZE +#define PIX_SIZE 1 +#endif + +//#define FULL_SCREEN 1 +#define NO_CURSOR 1 +#define NO_BORDER 1 + +/* + * light pen location + * see ws.h for full description + */ +int ws_lp_x = -1; +int ws_lp_y = -1; + +static XtAppContext app_context; /* the topmost context for everything */ +static Display* dpy; /* its display */ +static int scr; /* its screen */ +static Colormap cmap; /* its colormap */ +static Widget crtshell; /* the X window shell */ +static Widget crt; /* the X window in which output will plot */ +static int xpixels, ypixels; +#ifdef FULL_SCREEN +/* occupy entire screen for vintage computer fan Sellam Ismail */ +static int xoffset, yoffset; +#endif + +static GC whiteGC; /* gc with white foreground */ +static GC blackGC; /* gc with black foreground */ +static int buttons = 0; /* tracks state of all buttons */ + +static int os_pollfd(int, int); /* forward */ + +/* here on any mouse button down, AND movement when any button down */ +static void +handle_button_press(w, d, e, b) + Widget w; + XtPointer d; + XEvent *e; + Boolean *b; +{ + int x, y; + + x = e->xbutton.x; + y = e->xbutton.y; +#ifdef FULL_SCREEN + x -= xoffset; + y -= yoffset; +#endif +#if PIX_SIZE > 1 + x *= PIX_SIZE; + y *= PIX_SIZE; +#endif + +#ifndef NO_CURSUR + if (!display_tablet) + /* crosshair cursor to indicate tip of active pen */ + XDefineCursor(dpy, XtWindow(crt), + (Cursor) XCreateFontCursor(dpy, XC_crosshair)); +#endif + + y = ypixels - y - 1; + /*printf("lightpen at %d,%d\n", x, y); fflush(stdout);*/ + ws_lp_x = x; + ws_lp_y = y; + + if (e->type == ButtonPress) { + buttons |= e->xbutton.button; + + if (e->xbutton.button == 1) { + display_lp_sw = 1; + /*printf("tip switch activated\n"); fflush(stdout);*/ + } + } + + if (b) + *b = TRUE; +} + +static void +handle_button_release(w, d, e, b) + Widget w; + XtPointer d; + XEvent *e; + Boolean *b; +{ + if ((buttons &= ~e->xbutton.button) == 0) { /* all buttons released */ +#ifndef NO_CURSOR + if (!display_tablet) + /* pencil cursor (close to a pen!) to indicate inactive pen posn */ + XDefineCursor(dpy, XtWindow(crt), + (Cursor) XCreateFontCursor(dpy, XC_pencil)); +#endif + + /* XXX change cursor back?? */ + ws_lp_x = ws_lp_y = -1; + } + + if (e->xbutton.button == 1) { + display_lp_sw = 0; + /*printf("tip switch deactivated\n"); fflush(stdout);*/ + } + + if (b) + *b = TRUE; +} + +static void +handle_key_press(w, d, e, b) + Widget w; + XtPointer d; + XEvent *e; + Boolean *b; +{ + int shift = (ShiftMask & e->xkey.state) != 0; + KeySym key = XKeycodeToKeysym( dpy, e->xkey.keycode, shift ); + + /*printf("key %d down\n", key); fflush(stdout);*/ + if ((key & 0xff00) == 0) + display_keydown(key); + + if (b) + *b = TRUE; +} + +static void +handle_key_release(w, d, e, b) + Widget w; + XtPointer d; + XEvent *e; + Boolean *b; +{ + int shift = (ShiftMask & e->xkey.state) != 0; + KeySym key = XKeycodeToKeysym( dpy, e->xkey.keycode, shift ); + + /*printf("key %d up\n", key); fflush(stdout);*/ + if ((key & 0xff00) == 0) + display_keyup(key); + + if (b) + *b = TRUE; +} + +static void +handle_exposure(w, d, e, b) + Widget w; + XtPointer d; + XEvent *e; + Boolean *b; +{ + display_repaint(); + + if (b) + *b = TRUE; +} + +int +ws_init(char *crtname, /* crt type name */ + int xp, int yp, /* screen size in pixels */ + int colors) /* colors to support (not used) */ +{ + Arg arg[25]; + XGCValues gcvalues; + unsigned int n; + int argc; + char *argv[1]; + int height, width; + + xpixels = xp; /* save screen size */ + ypixels = yp; + + XtToolkitInitialize(); + app_context = XtCreateApplicationContext(); + argc = 0; + argv[0] = NULL; + dpy = XtOpenDisplay( app_context, NULL, NULL, crtname, NULL, 0, + &argc, argv); + + scr = DefaultScreen(dpy); + + crtshell = XtAppCreateShell( crtname, /* app name */ + crtname, /* app class */ + #ifdef NO_BORDER + overrideShellWidgetClass, + #else + applicationShellWidgetClass, /* wclass */ + #endif + dpy, /* display */ + NULL, /* arglist */ + 0); /* nargs */ + + cmap = DefaultColormap(dpy, scr); + + /* + * Create a drawing area + */ + + n = 0; +#ifdef FULL_SCREEN + /* center raster in full-screen black window */ + width = DisplayWidth(dpy,scr); + height = DisplayHeight(dpy,scr); + + xoffset = (width - xpixels*PIX_SIZE)/2; + yoffset = (height - ypixels*PIX_SIZE)/2; +#else + width = xpixels*PIX_SIZE; + height = ypixels*PIX_SIZE; +#endif + XtSetArg(arg[n], XtNwidth, width); n++; + XtSetArg(arg[n], XtNheight, height); n++; + XtSetArg(arg[n], XtNbackground, BlackPixel( dpy, scr )); n++; + + crt = XtCreateWidget( crtname, widgetClass, crtshell, arg, n); + XtManageChild(crt); + XtPopup(crtshell, XtGrabNonexclusive); + + /* + * Create black and white Graphics Contexts + */ + + gcvalues.foreground = BlackPixel( dpy, scr ); + gcvalues.background = BlackPixel( dpy, scr ); + blackGC = XCreateGC(dpy, XtWindow(crt), + GCForeground | GCBackground, &gcvalues); + + gcvalues.foreground = WhitePixel( dpy, scr ); + whiteGC = XCreateGC(dpy, XtWindow(crt), + GCForeground | GCBackground, &gcvalues); + +#ifndef NO_CURSOR + if (!display_tablet) { + /* pencil cursor */ + XDefineCursor(dpy, XtWindow(crt), + (Cursor) XCreateFontCursor(dpy, XC_pencil)); + } +#endif + + /* + * Setup to handle events + */ + + XtAddEventHandler(crt, ButtonPressMask|ButtonMotionMask, FALSE, + handle_button_press, NULL); + XtAddEventHandler(crt, ButtonReleaseMask, FALSE, + handle_button_release, NULL); + XtAddEventHandler(crt, KeyPressMask, FALSE, + handle_key_press, NULL); + XtAddEventHandler(crt, KeyReleaseMask, FALSE, + handle_key_release, NULL); + XtAddEventHandler(crt, ExposureMask, FALSE, + handle_exposure, NULL); + return 1; +} /* ws_init */ + + +/* Added 2006-07-19 SAI */ + +void ws_close(void) +{ + + XtCloseDisplay(dpy); + +} + + +void * +ws_color_black(void) +{ + return blackGC; +} + +void * +ws_color_white(void) +{ + return whiteGC; +} + +void * +ws_color_rgb(int r, int g, int b) +{ + XColor color; + + color.red = r; + color.green = g; + color.blue = b; + /* ignores flags */ + + if (XAllocColor(dpy, cmap, &color)) { + XGCValues gcvalues; + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.foreground = gcvalues.background = color.pixel; + return XCreateGC(dpy, XtWindow(crt), + GCForeground | GCBackground, + &gcvalues); + } + /* allocation failed */ + return NULL; +} + +/* put a point on the screen */ +void +ws_display_point(int x, int y, void *color) +{ + GC gc = (GC) color; + + if (x > xpixels || y > ypixels) + return; + + y = ypixels - y - 1; /* X11 coordinate system */ + +#ifdef FULL_SCREEN + x += xoffset; + y += yoffset; +#endif + if (gc == NULL) + gc = blackGC; /* default to off */ +#if PIX_SIZE == 1 + XDrawPoint(dpy, XtWindow(crt), gc, x, y); +#else + XFillRectangle(dpy, XtWindow(crt), gc, + x*PIX_SIZE, y*PIX_SIZE, PIX_SIZE, PIX_SIZE); +#endif +} + +void +ws_sync(void) +{ + XFlush(dpy); +} + +/* + * elapsed wall clock time since last call + * +INF on first call + */ + +struct elapsed_state { + struct timeval tvs[2]; + int new; +}; + +static unsigned long +elapsed(struct elapsed_state *ep) +{ + unsigned long val; + + gettimeofday(&ep->tvs[ep->new], NULL); + if (ep->tvs[!ep->new].tv_sec == 0) + val = ~0L; + else + val = ((ep->tvs[ep->new].tv_sec - ep->tvs[!ep->new].tv_sec) * 1000000 + + (ep->tvs[ep->new].tv_usec - ep->tvs[!ep->new].tv_usec)); + ep->new = !ep->new; + return val; +} + +/* called periodically */ +int +ws_poll(int *valp, int maxusec) +{ + static struct elapsed_state es; /* static to avoid clearing! */ + +#ifdef WS_POLL_DEBUG + printf("ws_poll %d\n", maxusec); + fflush(stdout); +#endif + elapsed(&es); /* start clock */ + do { + unsigned long e; + + /* tried checking return, but lost on TCP connections? */ + os_pollfd(ConnectionNumber(dpy), maxusec); + + while (XtAppPending(app_context)) { + XEvent event; + + /* XXX check for connection loss; set *valp? return 0 */ + XtAppNextEvent(app_context, &event ); + XtDispatchEvent( &event ); + } + e = elapsed(&es); +#ifdef WS_POLL_DEBUG + printf(" maxusec %d e %d\r\n", maxusec, e); + fflush(stdout); +#endif + maxusec -= e; + } while (maxusec > 10000); /* 10ms */ + return 1; +} + +/* utility: can be called from main program + * which is willing to cede control + */ +int +ws_loop(void (*func)(void *), void *arg) +{ + int val; + + /* XXX use XtAppAddWorkProc & XtAppMainLoop? */ + while (ws_poll(&val,0)) + (*func)(arg); + return val; +} + +void +ws_beep(void) +{ + XBell(dpy, 0); /* ring at base volume */ + XFlush(dpy); +} + +/**************** + * could move these to unix.c, if VMS versions needed + * (or just (GASP!) ifdef) + */ + +/* public version, used by delay code */ +unsigned long +os_elapsed(void) +{ + static struct elapsed_state es; + return elapsed(&es); +} + +/* + * select/DisplayNumber works on VMS 7.0+? + * could move to "unix.c" + * (I have some nasty VMS code that's supposed to to the job + * for older systems) + */ + +/* + * sleep for maxus microseconds, returning TRUE sooner if fd is readable + * used by X11 driver + */ +static int +os_pollfd(int fd, int maxus) +{ + + /* use trusty old select (most portable) */ + fd_set rfds; + struct timeval tv; + + if (maxus >= 1000000) { /* not bloody likely, avoid divide */ + tv.tv_sec = maxus / 1000000; + tv.tv_usec = maxus % 1000000; + } + else { + tv.tv_sec = 0; + tv.tv_usec = maxus; + } + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + return select(fd+1, &rfds, NULL, NULL, &tv) > 0; +}